Quellcode durchsuchen

Merge pull request #274 from bmx-ng/task/move-to-math

Moved to math.mod.
Brucey vor 2 Jahren
Ursprung
Commit
afc2aa9f1b
100 geänderte Dateien mit 0 neuen und 14739 gelöschten Zeilen
  1. 0 795
      matrix.mod/doc/intro.bbdoc
  2. 0 2962
      matrix.mod/matrix.bmx
  3. 0 30
      polygon.mod/common.bmx
  4. 0 27
      polygon.mod/earcut/CHANGELOG.md
  5. 0 151
      polygon.mod/earcut/CMakeLists.txt
  6. 0 15
      polygon.mod/earcut/LICENSE
  7. 0 131
      polygon.mod/earcut/README.md
  8. 0 33
      polygon.mod/earcut/appveyor.yml
  9. 0 816
      polygon.mod/earcut/include/mapbox/earcut.hpp
  10. 0 82
      polygon.mod/earcut/test/bench.cpp
  11. 0 43
      polygon.mod/earcut/test/comparison/earcut.hpp
  12. 0 105
      polygon.mod/earcut/test/comparison/libtess2.hpp
  13. 0 25
      polygon.mod/earcut/test/comparison/libtess2/LICENSE.txt
  14. 0 191
      polygon.mod/earcut/test/comparison/libtess2/bucketalloc.c
  15. 0 51
      polygon.mod/earcut/test/comparison/libtess2/bucketalloc.h
  16. 0 109
      polygon.mod/earcut/test/comparison/libtess2/dict.c
  17. 0 74
      polygon.mod/earcut/test/comparison/libtess2/dict.h
  18. 0 261
      polygon.mod/earcut/test/comparison/libtess2/geom.c
  19. 0 76
      polygon.mod/earcut/test/comparison/libtess2/geom.h
  20. 0 843
      polygon.mod/earcut/test/comparison/libtess2/mesh.c
  21. 0 267
      polygon.mod/earcut/test/comparison/libtess2/mesh.h
  22. 0 514
      polygon.mod/earcut/test/comparison/libtess2/priorityq.c
  23. 0 104
      polygon.mod/earcut/test/comparison/libtess2/priorityq.h
  24. 0 1326
      polygon.mod/earcut/test/comparison/libtess2/sweep.c
  25. 0 74
      polygon.mod/earcut/test/comparison/libtess2/sweep.h
  26. 0 982
      polygon.mod/earcut/test/comparison/libtess2/tess.c
  27. 0 90
      polygon.mod/earcut/test/comparison/libtess2/tess.h
  28. 0 221
      polygon.mod/earcut/test/comparison/libtess2/tesselator.h
  29. 0 79
      polygon.mod/earcut/test/convert_tests.js
  30. 0 13
      polygon.mod/earcut/test/fixtures/bad_diagonals.cpp
  31. 0 16
      polygon.mod/earcut/test/fixtures/bad_hole.cpp
  32. 0 17
      polygon.mod/earcut/test/fixtures/boxy.cpp
  33. 0 13
      polygon.mod/earcut/test/fixtures/building.cpp
  34. 0 13
      polygon.mod/earcut/test/fixtures/collinear_diagonal.cpp
  35. 0 13
      polygon.mod/earcut/test/fixtures/degenerate.cpp
  36. 0 8
      polygon.mod/earcut/test/fixtures/dude.cpp
  37. 0 15
      polygon.mod/earcut/test/fixtures/eberly_3.cpp
  38. 0 8
      polygon.mod/earcut/test/fixtures/eberly_6.cpp
  39. 0 14
      polygon.mod/earcut/test/fixtures/empty_square.cpp
  40. 0 17
      polygon.mod/earcut/test/fixtures/filtered_bridge_jhl.cpp
  41. 0 106
      polygon.mod/earcut/test/fixtures/geometries.hpp
  42. 0 8
      polygon.mod/earcut/test/fixtures/hilbert.cpp
  43. 0 14
      polygon.mod/earcut/test/fixtures/hole_touching_outer.cpp
  44. 0 13
      polygon.mod/earcut/test/fixtures/hourglass.cpp
  45. 0 14
      polygon.mod/earcut/test/fixtures/infinite_loop_jhl.cpp
  46. 0 14
      polygon.mod/earcut/test/fixtures/issue107.cpp
  47. 0 16
      polygon.mod/earcut/test/fixtures/issue111.cpp
  48. 0 17
      polygon.mod/earcut/test/fixtures/issue119.cpp
  49. 0 15
      polygon.mod/earcut/test/fixtures/issue131.cpp
  50. 0 14
      polygon.mod/earcut/test/fixtures/issue135.cpp
  51. 0 14
      polygon.mod/earcut/test/fixtures/issue142.cpp
  52. 0 14
      polygon.mod/earcut/test/fixtures/issue149.cpp
  53. 0 14
      polygon.mod/earcut/test/fixtures/issue16.cpp
  54. 0 14
      polygon.mod/earcut/test/fixtures/issue17.cpp
  55. 0 14
      polygon.mod/earcut/test/fixtures/issue29.cpp
  56. 0 20
      polygon.mod/earcut/test/fixtures/issue34.cpp
  57. 0 8
      polygon.mod/earcut/test/fixtures/issue35.cpp
  58. 0 15
      polygon.mod/earcut/test/fixtures/issue45.cpp
  59. 0 18
      polygon.mod/earcut/test/fixtures/issue52.cpp
  60. 0 15
      polygon.mod/earcut/test/fixtures/issue83.cpp
  61. 0 15
      polygon.mod/earcut/test/fixtures/outside_ring.cpp
  62. 0 8
      polygon.mod/earcut/test/fixtures/rain.cpp
  63. 0 8
      polygon.mod/earcut/test/fixtures/self_touching.cpp
  64. 0 13
      polygon.mod/earcut/test/fixtures/shared_points.cpp
  65. 0 14
      polygon.mod/earcut/test/fixtures/simplified_us_border.cpp
  66. 0 17
      polygon.mod/earcut/test/fixtures/steiner.cpp
  67. 0 14
      polygon.mod/earcut/test/fixtures/touching2.cpp
  68. 0 15
      polygon.mod/earcut/test/fixtures/touching3.cpp
  69. 0 17
      polygon.mod/earcut/test/fixtures/touching4.cpp
  70. 0 20
      polygon.mod/earcut/test/fixtures/touching_holes.cpp
  71. 0 8
      polygon.mod/earcut/test/fixtures/water.cpp
  72. 0 8
      polygon.mod/earcut/test/fixtures/water2.cpp
  73. 0 18
      polygon.mod/earcut/test/fixtures/water3.cpp
  74. 0 15
      polygon.mod/earcut/test/fixtures/water3b.cpp
  75. 0 8
      polygon.mod/earcut/test/fixtures/water4.cpp
  76. 0 8
      polygon.mod/earcut/test/fixtures/water_huge.cpp
  77. 0 8
      polygon.mod/earcut/test/fixtures/water_huge2.cpp
  78. 0 70
      polygon.mod/earcut/test/tap.cpp
  79. 0 27
      polygon.mod/earcut/test/tap.hpp
  80. 0 117
      polygon.mod/earcut/test/test.cpp
  81. 0 510
      polygon.mod/earcut/test/viz.cpp
  82. 0 15
      polygon.mod/examples/example_01.bmx
  83. 0 33
      polygon.mod/examples/example_02.bmx
  84. 0 102
      polygon.mod/glue.cpp
  85. 0 61
      polygon.mod/polygon.bmx
  86. 0 203
      quaternion.mod/doc/intro.bbdoc
  87. 0 2
      quaternion.mod/glue.h
  88. 0 1445
      quaternion.mod/quaternion.bmx
  89. 0 248
      quaternion.mod/tests/test.bmx
  90. 0 578
      vector.mod/doc/intro.bbdoc
  91. 0 12
      vector.mod/doc/svec2d_angleto.bmx
  92. 0 14
      vector.mod/doc/svec2d_clamp.bmx
  93. 0 11
      vector.mod/doc/svec2d_dot.bmx
  94. 0 11
      vector.mod/doc/svec2d_interpolate.bmx
  95. 0 10
      vector.mod/doc/svec2d_length.bmx
  96. 0 10
      vector.mod/doc/svec2d_lengthsquared.bmx
  97. 0 12
      vector.mod/doc/svec2d_max.bmx
  98. 0 12
      vector.mod/doc/svec2d_min.bmx
  99. 0 10
      vector.mod/doc/svec2d_normal.bmx
  100. 0 11
      vector.mod/doc/svec2d_operator_add.bmx

+ 0 - 795
matrix.mod/doc/intro.bbdoc

@@ -1,795 +0,0 @@
-Matrices are fundamental mathematical constructs used in various fields, such as computer graphics,
-physics simulations, and linear algebra. They are especially useful for representing linear transformations,
-including rotations, translations, and scaling operations. In the context of graphics and 3D geometry,
-matrices are commonly used to manipulate vertices and objects in a scene, transforming their coordinates
-and orientations.
-
-A matrix is a rectangular array of numbers, organized into rows and columns. In BlitzMax,
-`brl.matrix` provides several structs to work with matrices of different
-sizes and types, such as #SMat2D, #SMat3D, #SMat4D, #SMat2F, #SMat3F, #SMat4F, #SMat2I, #SMat3I, and #SMat4I.
-The name of each struct indicates its size and the primitive type it uses: #Double, #Float, or #Int.
-In our examples, we'll focus primarily on the #Double structs, but the same concepts apply to the other
-structs as well.
-
-The following is a brief introduction to the three main types of matrices used in graphics and 3D geometry, which
-BlitzMax provides structs for, along with a high-level overview of their applications:
-
-## Types of Matrices
-
-### 2x2 Matrices
-A 2x2 matrix, represented by the #SMat2D, #SMat2F and #SMat2I structs, is primarily used for 2D linear transformations,
-such as rotation and scaling. These matrices can be applied to 2D vectors, transforming their
-coordinates accordingly. For example, a 2x2 rotation matrix can be used to rotate a 2D vector
-around the origin by a specified angle.
-
-### 3x3 Matrices
-3x3 matrices, represented by the #SMat3D, #SMat3F and #SMat3I structs, are commonly used for 3D linear transformations that do
-not involve translation, such as rotation and scaling. These matrices can be applied to 3D vectors,
-transforming their coordinates without changing their position in the scene. For example, a 3x3 rotation
-matrix can be used to rotate a 3D vector around a specific axis by a given angle.
-
-The following example demonstrates the use of 3x3 matrices, by creating a simple solar system. The sun
-is represented by a yellow circle, and the planets are represented by blue circles. The planets are
-rotating around the sun, and their positions are calculated using 3x3 matrices.
-
-```blitzmax
-SuperStrict
-
-Framework SDL.SDLRenderMax2D
-Import brl.matrix
-
-Graphics 800, 600, 0
-
-Local sunX:Float = GraphicsWidth() / 2
-Local sunY:Float = GraphicsHeight() / 2
-Local sunRadius:Float = 50
-
-Local planets:TPlanet[] = [New TPlanet(150, 20, 0.7), New TPlanet(200, 15, 0.5), New TPlanet(250, 10, 0.3)]
-
-While Not KeyHit(KEY_ESCAPE)
-    Cls
-
-    ' Draw the sun
-    SetColor(255, 255, 0)
-    DrawOval(sunX - sunRadius, sunY - sunRadius, sunRadius * 2, sunRadius * 2)
-
-    ' Draw the planets
-    For Local planet:TPlanet = EachIn planets
-        planet.Draw(sunX, sunY)
-    Next
-
-    Flip
-Wend
-
-Type TPlanet
-    Field distance:Float
-    Field radius:Float
-    Field angle:Float
-    Field speed:Float
-
-    Method New(distance:Float, radius:Float, speed:Float)
-        Self.distance = distance
-        Self.radius = radius
-        Self.speed = speed
-    End Method
-
-	Method Draw(centerX:Float, centerY:Float)
-		' Calculate the planet's position
-		Local planetMatrix:SMat3D = SMat3D.Identity()
-		planetMatrix = planetMatrix.Rotate(angle)
-		planetMatrix = planetMatrix.Translate(New SVec3D(distance, 0, 0))
-		Local planetPos:SVec3D = planetMatrix.Apply(New SVec3D(0, 0, 1))
-	
-		' Draw the planet
-		SetColor(0, 0, 255)
-		DrawOval(Float(centerX + planetPos.x - radius), Float(centerY + planetPos.y - radius), radius * 2, radius * 2)
-	
-		' Update the angle for the next frame
-		angle :+ speed
-	End Method
-End Type
-```
-
-### 4x4 Matrices
-4x4 matrices, represented by the #SMat4D, #SMat4F and #SMat4I structs, are the most versatile and widely used matrices in 3D
-graphics. They can represent any combination of linear transformations, such as rotation, scaling,
-and translation. A 4x4 matrix can be applied to a 3D vector, transforming its position, orientation,
-and scale in the scene. For example, a 4x4 transformation matrix can be used to move an object in 3D space,
-rotate it around a specific axis, and scale it uniformly or non-uniformly.
-
-Creating a 3D-like effect in 2D can be achieved using simple orthogonal projection. In this example,
-we will create a rotating cube using a 4x4 transformation matrix to apply rotation, scaling, and translation. 
-
-```blitzmax
-SuperStrict
-
-Framework SDL.SDLRenderMax2D
-Import brl.matrix
-
-Graphics 800, 600, 0
-
-Local cube:TCube = New TCube
-Local angle:Float = 0
-
-Local centerX:Int = GraphicsWidth() / 2
-Local centerY:Int = GraphicsHeight() / 2
-
-While Not KeyHit(KEY_ESCAPE)
-    Cls
-
-    ' Create the transformation matrix..
-    ' Start with an identity matrix as the initial transformation.
-    Local matrix:SMat4D = SMat4D.Identity()
-    ' Move the object away from the camera along the Z-axis by a distance of 6 units.
-    ' This translation ensures that the object is visible and centered in the view.
-    matrix = matrix.Translate(New SVec3D(0, 0, -6))
-    ' Rotate the object around the Z-axis (blue axis) by an angle scaled by 1.2.
-    ' This rotation is applied first to achieve a "rolling" effect.
-    matrix = matrix.Rotate(New SVec3D(0, 0, 1), angle * 1.2)
-    ' Rotate the object around the Y-axis (green axis) by an angle scaled by 0.7.
-    ' This rotation is applied after the first rotation, creating a "pitching" effect.
-    matrix = matrix.Rotate(New SVec3D(0, 1, 0), angle * 0.7)
-    ' Finally, rotate the object around the X-axis (red axis) by the base angle.
-    ' This rotation is applied last, creating a "yawing" effect.
-    matrix = matrix.Rotate(New SVec3D(1, 0, 0), angle)
-
-    ' Draw cube
-    cube.Draw(matrix, 100, centerX, centerY)
-
-    Flip
-    angle :+ 0.5
-Wend
-
-Type TCube
-    Field vertices:Float[24] ' 8 vertices, each with 3 coordinates
-    Field edges:Int[24] ' 12 edges, each with 2 vertex indices
-
-    Method New()
-        vertices = [ -1:Float, -1:Float, -1:Float, 1:Float, -1:Float, -1:Float, 1:Float, 1:Float, -1:Float, -1:Float, ..
-					1:Float, -1:Float, -1:Float, -1:Float, 1:Float, 1:Float, -1:Float, 1:Float, 1:Float, 1:Float, ..
-					1:Float, -1:Float, 1:Float, 1:Float ]
-        edges = [ 0, 1, 1, 2, 2, 3, 3, 0, ..
-                  4, 5, 5, 6, 6, 7, 7, 4, ..
-                  0, 4, 1, 5, 2, 6, 3, 7 ]
-    End Method
-
-    Method Draw(matrix:SMat4D, scale:Float = 1, centerX:Int, centerY:Int)
-        Local transformedVertices:Float[24]
-
-		' Apply transformation and orthographic projection
-		For Local i:Int = 0 Until 24 Step 3
-			Local vertex:SVec3D = New SVec3D(vertices[i], vertices[i + 1], vertices[i + 2])
-			vertex = matrix.Apply(vertex)
-			transformedVertices[i] = centerX + (scale * vertex.x)
-			transformedVertices[i + 1] = centerY - (scale * vertex.y)
-		Next
-
-		' Draw edges
-		For Local i:Int = 0 Until 24 Step 2
-			DrawLine(transformedVertices[edges[i] * 3], transformedVertices[edges[i] * 3 + 1],
-					transformedVertices[edges[i + 1] * 3], transformedVertices[edges[i + 1] * 3 + 1])
-		Next
-
-    End Method
-End Type
-```
-
-## Column-Major vs. Row-Major Matrices
-
-In `brl.matrix`, we use column-major ordering for matrices. Column-major ordering is a convention
-in which elements are stored column by column in memory. It is the opposite of row-major ordering, where
-elements are stored row by row. This distinction is important when performing operations like matrix
-multiplication or when applying transformations to vectors or other matrices.
-
-Column-major ordering is commonly used in graphics programming and some mathematical libraries. In
-this convention, when applying transformations, the order in which they are applied is reversed
-compared to row-major ordering. This means that when you want to apply multiple transformations,
-you should apply them in the reverse order that you would with row-major ordering.
-
-For example, in column-major ordering, to rotate an object around the X, Y, and Z axes and then
-translate it, you would first apply the translation, then the rotation around the Z axis, followed
-by the rotation around the Y axis, and finally the rotation around the X axis.
-
-## Determinant, Inverse, and Transpose in Matrices
-
-### Determinant
-
-The determinant is a scalar value associated with square matrices that carries essential
-information about the matrix's properties. It has several important geometric interpretations
-and applications in linear algebra.
-
-In the context of 2D transformations, the determinant represents the area scaling factor when a
-matrix is applied to a vector. If the determinant is positive, the transformation preserves the
-orientation; if it's negative, the orientation is reversed. A determinant equal to zero indicates
-that the transformation collapses the vector onto a lower-dimensional subspace (e.g., a line or a point),
-making the matrix singular and non-invertible.
-
-Mathematically, the determinant of a 2x2 matrix is calculated as follows:
-
-```
-|A| = |a  b|
-      |c  d|
-
-|A| = ad - bc
-```
-
-Here's an example illustrating how to calculate the determinant of a 2x2 matrix using the #SMat2D struct:
-
-```blitzmax
-SuperStrict
-
-Framework BRL.StandardIO
-Import BRL.Matrix
-
-' Define matrix elements
-Local a:Double = 3.0
-Local b:Double = 2.0
-Local c:Double = 5.0
-Local d:Double = 4.0
-
-' Create a 2x2 matrix
-Local mat:SMat2D = New SMat2D(a, b, c, d)
-
-' Calculate the determinant
-Local det:Double = mat.Determinant()
-
-' Print the determinant
-Print "Determinant: " + det
-```
-When you run this example, it will create a 2x2 matrix with the specified elements,
-calculate the determinant using the `Determinant()` method provided by #SMat2D, and print the resulting
-determinant value. In this case, the output will be:
-```
-Determinant: -2.0
-```
-
-For a 3x3 matrix, the determinant can be calculated using the following formula:
-```
-|A| = |a  b  c|
-      |d  e  f|
-      |g  h  i|
-
-|A| = a(ei - fh) - b(di - fg) + c(dh - eg)
-```
-
-Here's an example illustrating how to calculate the determinant of a 3x3 matrix using the #SMat3D struct:
-```blitzmax
-SuperStrict
-
-Framework BRL.StandardIO
-Import BRL.Matrix
-
-' Define matrix elements
-Local a:Double = 1.0
-Local b:Double = 2.0
-Local c:Double = 3.0
-Local d:Double = 4.0
-Local e:Double = 5.0
-Local f:Double = 6.0
-Local g:Double = 7.0
-Local h:Double = 8.0
-Local i:Double = 9.0
-
-' Create a 3x3 matrix
-Local mat:SMat3D = New SMat3D(a, b, c, d, e, f, g, h, i)
-
-' Calculate the determinant
-Local det:Double = mat.Determinant()
-
-' Print the determinant
-Print "Determinant: " + det
-```
-
-When you run this example, it will create a 3x3 matrix with the specified elements,
-calculate the determinant using the `Determinant()` method provided by #SMat3D, and print the
-resulting determinant value. In this case, the output will be:
-```
-Determinant: 0.0
-```
-
-### Inverse
-
-The inverse of a matrix is another matrix that, when multiplied with the original matrix,
-results in the identity matrix. In other words, for a matrix A, its inverse A⁻¹ exists if
-and only if A * A⁻¹ = A⁻¹ * A = I, where I is the identity matrix. Not all matrices have
-inverses; only square matrices (i.e., matrices with the same number of rows and columns) may
-have inverses, and even then, not all square matrices are invertible.
-
-For a 2x2 matrix:
-```
-| a  b |
-| c  d |
-```
-The inverse can be calculated using the following formula:
-```
-1 / (ad - bc) * |  d  -b |
-                | -c   a |
-```
-
-The term (ad - bc) is the determinant of the matrix. If the determinant is zero, the matrix
-is singular and does not have an inverse.
-
-For a 3x3 matrix, the process is more complicated and involves finding the matrix of minors,
-the matrix of cofactors, and finally, the adjugate matrix. The inverse can then be computed
-by dividing the adjugate matrix by the determinant of the original matrix.
-
-Here is an example for finding the inverse of a 2x2 matrix using the #SMat2D struct:
-```blitzmax
-SuperStrict
-
-Framework brl.standardio
-Import brl.matrix
-
-' Create a 2x2 matrix
-Local a:Double = 3
-Local b:Double = 2
-Local c:Double = 5
-Local d:Double = 3
-
-Local mat:SMat2D = New SMat2D(a, b, c, d)
-
-' Calculate the inverse
-Local matInverse:SMat2D = mat.Invert()
-
-' Print the original and inverse matrices
-Print "Original matrix:~n" + mat.ToString()
-Print "Inverse matrix:~n" + matInverse.ToString()
-```
-When you run this example, it will output the original 2x2 matrix and its inverse:
-```
-Original matrix:
-3.000 2.000
-5.000 3.000
-Inverse matrix:
--1.000 0.667
- 1.667 -1.000
-```
-
-
-And here is an example for finding the inverse of a 3x3 matrix using the #SMat3D struct:
-```blitzmax
-SuperStrict
-
-Framework brl.standardio
-Import brl.matrix
-
-' Create a 3x3 matrix
-Local a:Double = 1
-Local b:Double = 2
-Local c:Double = 3
-Local d:Double = 0
-Local e:Double = 1
-Local f:Double = 4
-Local g:Double = 5
-Local h:Double = 6
-Local i:Double = 0
-
-Local mat:SMat3D = New SMat3D(a, b, c, d, e, f, g, h, i)
-
-' Calculate the inverse
-Local matInverse:SMat3D = mat.Invert()
-
-' Print the original and inverse matrices
-Print "Original matrix: " + mat.ToString()
-Print "Inverse matrix: " + matInverse.ToString()
-```
-When you run this example, it will output the original 3x3 matrix and its inverse:
-```
-Original matrix:
- 1.000 2.000 3.000
- 0.000 1.000 4.000
- 5.000 6.000 0.000
-Inverse matrix:
- -24.000 18.000 5.000
- 20.000 -15.000 -4.000
- -5.000 4.000 1.000
-```
-
-### Transpose
-
-The transpose of a matrix is a new matrix obtained by interchanging its rows and columns.
-In other words, the transpose of a matrix A is a new matrix Aᵀ, where the element Aᵀ[i, j] = A[j, i].
-The transpose operation has several important properties and applications in linear algebra, including
-simplifying matrix equations and working with symmetric matrices.
-
-For a 2x2 matrix A:
-```
-A = |a  b|       Aᵀ = |a  c|
-    |c  d|            |b  d|
-```
-
-For a 3x3 matrix A:
-```
-A = |a  b  c|       Aᵀ = |a  d  g|
-    |d  e  f|            |b  e  h|
-    |g  h  i|            |c  f  i|
-```
-
-Here's an example illustrating how to calculate the transpose of a 2x2 matrix using the #SMat2D struct.
-```blitzmax
-SuperStrict
-
-Framework brl.standardIO
-Import brl.matrix
-
-' Define matrix elements
-Local a:Double = 1.0
-Local b:Double = 2.0
-Local c:Double = 3.0
-Local d:Double = 4.0
-
-' Create a 2x2 matrix
-Local mat:SMat2D = New SMat2D(a, b, c, d)
-
-' Calculate the transpose
-Local matTranspose:SMat2D = mat.Transpose()
-
-' Print the original and transposed matrices
-Print "Original matrix:~n" + mat.ToString()
-Print "Transposed matrix:~n" + matTranspose.ToString()
-```
-When you run this example, it will create a 2x2 matrix with the specified elements, calculate
-the transpose using the Transpose() method provided by the #SMat2D struct, and print the original
-and transposed matrices. In this case, the output will be:
-```
-Original matrix:
-1.0,  2.0
-3.0,  4.0
-Transposed matrix:
-1.0  3.0
-2.0  4.0
-```
-
-And an example for calculating the transpose of a 3x3 matrix using the #SMat3D struct.
-```blitzmax
-SuperStrict
-
-Framework brl.standardIO
-Import brl.matrix
-
-' Define matrix elements
-Local a:Double = 1.0
-Local b:Double = 2.0
-Local c:Double = 3.0
-Local d:Double = 4.0
-Local e:Double = 5.0
-Local f:Double = 6.0
-Local g:Double = 7.0
-Local h:Double = 8.0
-Local i:Double = 9.0
-
-' Create a 3x3 matrix
-Local mat:SMat3D = New SMat3D(a, b, c, d, e, f, g, h, i)
-
-' Calculate the transpose
-Local matTranspose:SMat3D = mat.Transpose()
-
-' Print the original and transposed matrices
-Print "Original matrix: " + mat.ToString()
-Print "Transposed matrix: " + matTranspose.ToString()
-```
-
-When you run this example, it will create a 3x3 matrix with the specified elements,
-calculate the transpose using the Transpose() method provided by the #SMat3D struct, and print the
-original and transposed matrices. In this case, the output will be:
-```
-Original matrix:
-1.0  2.0  3.0
-4.0  5.0  6.0
-7.0  8.0  9.0
-Transposed matrix:
-1.0  4.0  7.0
-2.0  5.0  8.0
-3.0  6.0  9.0
-```
-
-
-Understanding the transpose concept and its properties is important when working with matrices
-in your BlitzMax projects. The transpose operation can help simplify matrix equations and work with
-symmetric matrices, among other applications. By transposing a matrix, you can manipulate its elements
-and apply various transformations as required in your project.
-
-## Scaling, rotation, and shearing matrices
-
-Scaling, rotation, and shearing matrices are fundamental linear transformations used in various fields, such as
-computer graphics, physics simulations, and geometric modeling. These transformations can be applied to vectors or points
-to change their position, orientation, or shape. 
-
-### Scaling matrices
-
-Scaling is the process of resizing an object uniformly or non-uniformly along its axes. Scaling matrices are diagonal matrices
-that have scaling factors along the diagonal elements, and zeros elsewhere.
-
-For 2D scaling:
-
-```
-| sx  0  |
-| 0   sy |
-```
-
-For 3D scaling:
-```
-| sx  0   0  |
-| 0   sy  0  |
-| 0   0   sz |
-```
-
-Here, sx, sy, and sz are the scaling factors along the x, y, and z axes, respectively. To apply the scaling transformation, you multiply
-the scaling matrix by the vector or point you want to scale.
-
-### Rotation matrices
-
-Rotation is the process of rotating an object around a point or an axis. Rotation matrices are used to represent the rotation
-transformations in both 2D and 3D spaces.
-
-For 2D rotation around the origin by angle θ:
-
-```
-| cos(θ)  -sin(θ) |
-| sin(θ)   cos(θ) |
-```
-
-For 3D rotation around an arbitrary axis (x, y, z) by angle θ, the rotation matrix is more complex. One common method is using
-the axis-angle representation, which involves the Rodrigues' rotation formula. The formula for the 3D rotation matrix is as follows:
-
-```
-R = I + sin(θ) * K + (1 - cos(θ)) * K^2
-```
-
-Where `I` is the identity matrix, `θ` is the angle of rotation, and `K` is the skew-symmetric matrix representation of the axis vector.
-
-### Shearing matrices
-
-Shearing is the process of transforming an object by displacing its points along one axis based on the displacement of points along a
-parallel axis. Shearing matrices are used to represent these transformations.
-
-For 2D shearing:
-
-```
-| 1  shx |
-| shy  1 |
-```
-
-For 3D shearing along the x-axis:
-
-```
-| 1  shy  shz |
-| 0   1    0  |
-| 0   0    1  |
-```
-
-And similar matrices can be defined for shearing along the y-axis and z-axis. Here, `shx`, `shy`, and `shz` represent the shearing factors.
-
-When applying these transformations, it's important to consider the order of operations, as matrix multiplication is not commutative.
-Typically, scaling and shearing are applied first, followed by rotation, and finally translation. Also, remember that these matrices should
-be applied to homogeneous coordinates for translation to work correctly. In 3D graphics, this often involves using 4x4 matrices with an
-additional row and column for the homogeneous coordinate.
-
-## Matrix multiplication
-
-Matrix multiplication is an essential operation in linear algebra and has numerous applications in computer graphics, physics, and other fields.
-Multiplying two matrices involves specific rules, and it's important to understand the dimensions of the matrices and how to perform the
-multiplication element-wise.
-
-### Rules for Matrix Multiplication
-
-Given two matrices A and B, the product AB can only be computed if the number of columns in A is equal to the number of rows in B.
-If matrix A has dimensions m × n and matrix B has dimensions n × p, then the product AB will have dimensions m × p.
-
-`(AB)_ij = A_i1 * B_1j + A_i2 * B_2j + ... + A_in * B_nj`
-
-where (AB)_ij represents the element in the i-th row and j-th column of the resulting matrix AB.
-
-### Element-wise matrix multiplication
-
-To multiply two matrices element-wise, follow these steps:
-
-1. Verify that the dimensions of the matrices are compatible for multiplication. The number of columns in matrix A must be equal
-to the number of rows in matrix B.
-
-2. Create a new matrix C with dimensions m × p, where m is the number of rows in matrix A and p is the number of columns in matrix B.
-
-3. For each element (i, j) in the resulting matrix C, calculate the value as the sum of the products of the corresponding row elements in matrix A
-and the column elements in matrix B:
-`C_ij = A_i1 * B_1j + A_i2 * B_2j + ... + A_in * B_nj`
-Iterate through all elements in matrix C, computing their values using the above formula.
-
-4. The resulting matrix C represents the product of matrices A and B.
-
-It's important to note that matrix multiplication is not commutative, meaning that AB ≠ BA in general. However, it is
-associative (A(BC) = (AB)C) and distributive over addition (A(B+C) = AB + AC).
-
-The `CompMul()` method can be used to perform element-wise matrix multiplication.
-
-### Matrix multiplication using the dot product
-
-Matrix multiplication is a fundamental operation in linear algebra, and it is performed using the dot product.
-It is important to understand that matrix multiplication is not the same as element-wise multiplication. In matrix
-multiplication, the dot product of the rows of the first matrix and the columns of the second matrix is used to calculate the resulting matrix elements.
-
-Consider two matrices, A and B, where A has dimensions m x n (m rows and n columns), and B has dimensions p x q (p rows and q columns).
-For matrix multiplication to be possible, the number of columns in A (n) must be equal to the number of rows in B (p). The resulting
-matrix, C, will have dimensions m x q (m rows and q columns).
-
-Here is the formula for matrix multiplication using the dot product:
-
-C[i][j] = Σ (A[i][k] * B[k][j]) for k = 0 to n-1
-
-where i ranges from 0 to m-1 and j ranges from 0 to q-1.
-
-In other words, each element in the resulting matrix C is the dot product of the i-th row of matrix A and the j-th column of matrix B.
-
-Let's see an example with two 3x3 matrices:
-
-```
-A = | a b c |     B = | p q r |
-    | d e f |         | s t u |
-    | g h i |         | v w x |
-
-C = | (a*p + b*s + c*v) (a*q + b*t + c*w) (a*r + b*u + c*x) |
-    | (d*p + e*s + f*v) (d*q + e*t + f*w) (d*r + e*u + f*x) |
-    | (g*p + h*s + i*v) (g*q + h*t + i*w) (g*r + h*u + i*x) |
-```
-
-The `Operator *()` method can be used to perform matrix multiplication using the dot product.
-
-### Vector Multiplication
-
-Matrix-vector multiplication is a fundamental operation in linear algebra, computer graphics, and many other fields. This operation
-is used to apply transformations, such as scaling, rotation, and translation, to points and vectors in 2D and 3D space. In this section,
-we will discuss how to multiply a matrix by a vector and how this operation can be applied to 2D and 3D vectors in BlitzMax.
-
-#### 2D Vectors ( #SVec2D, #SVec2F, #SVec2I )
-
-To multiply a 2x2 matrix by a 2D vector, perform the following steps:
-
-1. Multiply each element of the first row of the matrix by the corresponding element of the vector.
-2. Sum the results from step 1 to obtain the first element of the resulting vector.
-3. Multiply each element of the second row of the matrix by the corresponding element of the vector.
-4. Sum the results from step 3 to obtain the second element of the resulting vector.
-
-Mathematically, this can be represented as:
-
-```
-| a c | | x |   | a * x + c * y |
-| b d | | y | = | b * x + d * y |
-```
-
-#### 3D Vectors ( #SVec3D, #SVec3F, #SVec3I )
-
-To multiply a 3x3 matrix by a 3D vector, follow these steps:
-
-1. Multiply each element of the first row of the matrix by the corresponding element of the vector.
-2. Sum the results from step 1 to obtain the first element of the resulting vector.
-3. Multiply each element of the second row of the matrix by the corresponding element of the vector.
-4. Sum the results from step 3 to obtain the second element of the resulting vector.
-5. Multiply each element of the third row of the matrix by the corresponding element of the vector.
-6. Sum the results from step 5 to obtain the third element of the resulting vector.
-
-Mathematically, this can be represented as:
-
-```
-| a d g | | x |   | a * x + d * y + g * z |
-| b e h | | y | = | b * x + e * y + h * z |
-| c f i | | z |   | c * x + f * y + i * z |
-```
-
-You can use the `Apply()` method to multiply a matrix by a vector.
-
-Here's an example:
-
-```blitzmax
-Local mat2D:SMat2D = SMat2D.Identity()
-Local vec2D:SVec2D = New SVec2D(2, 3)
-Local result2D:SVec2D = mat2D.Apply(vec2D)
-
-Local mat3D:SMat3D = SMat3D.Identity()
-Local vec3D:SVec3D = New SVec3D(2, 3, 4)
-Local result3D:SVec3D = mat3D.Apply(vec3D)
-```
-
-## The Identity Matrix
-
-The identity matrix is a special square matrix that has ones on the diagonal and zeros elsewhere. In mathematical notation,
-an identity matrix of size n x n is denoted as Iₙ. Here's an example of a 3x3 identity matrix:
-
-```
-I₃ = | 1 0 0 |
-     | 0 1 0 |
-     | 0 0 1 |
-```
-
-The identity matrix plays a crucial role in matrix algebra, as it serves as the "neutral element" for matrix multiplication. When you
-multiply any matrix by the identity matrix, the result is the original matrix unchanged. Mathematically, this property can be written as:
-
-A * Iₙ = A
-
-Iₙ * A = A
-
-where A is any n x n matrix.
-
-This property is analogous to the number 1 in scalar multiplication. Just as multiplying any number by 1 leaves the number unchanged
-(e.g., 7 * 1 = 7), multiplying any matrix by the identity matrix leaves the matrix unchanged.
-
-The identity matrix is also important for other matrix operations. For example, when calculating the inverse of a matrix, the goal is to
-find a matrix B such that A * B = Iₙ. When this condition is met, matrix B is the inverse of matrix A.
-
-The `Identity()` method allows you create an identity matrix.
-
-## Linear Transformations
-
-Matrices are often used to represent linear transformations in various fields, including computer graphics and linear algebra.
-A linear transformation is a function that maps vectors from one vector space to another while preserving the operations of vector
-addition and scalar multiplication. In this section, we will discuss some of the key properties of linear transformations and how
-matrices can be used to represent them.
-
-### Linearity
-
-A linear transformation, T, has the property of linearity, which means it satisfies the following conditions:
-
-1. T(u + v) = T(u) + T(v) for all vectors u and v.
-2. T(c * u) = c * T(u) for all vectors u and any scalar c.
-
-This property ensures that the transformation maintains the structure of the vector space, preserving
-parallelism and proportions between the vectors.
-
-### Invertibility
-
-A linear transformation is invertible if there exists another transformation, called its inverse, that can undo the effect of the original
-transformation. In terms of matrices, a matrix A is invertible if there exists a matrix B such that AB = BA = I, where I is the identity matrix.
-Invertible transformations are essential in many applications, as they allow us to reverse the effects of a transformation and recover the original data.
-
-### Effect on Geometry
-
-Linear transformations can have various effects on the geometry of shapes, including:
-
-1. Scaling: A transformation that changes the size of a shape, either uniformly or non-uniformly along different axes.
-2. Rotation: A transformation that rotates a shape around a specified point or axis.
-3. Shearing: A transformation that distorts a shape by shifting its points along one axis relative to the other axis.
-
-## Orthogonal and Orthonormal Matrices
-
-Orthogonal and orthonormal matrices are special types of square matrices that have unique properties, making them particularly useful in
-various applications such as computer graphics, physics, and linear algebra.
-
-### Orthogonal Matrices
-
-A matrix A is called an orthogonal matrix if its transpose, A^T, is also its inverse, i.e., A^T * A = A * A^T = I, where I is the identity
-matrix. In other words, the columns (and rows) of an orthogonal matrix are orthogonal to each other, meaning they form a 90-degree angle
-and their dot product is zero.
-
-The geometric interpretation of orthogonal matrices is that they represent transformations that preserve lengths and angles, such as
-rotations and reflections.
-
-### Orthonormal Matrices
-
-An orthonormal matrix is an orthogonal matrix where the columns (and rows) are not only orthogonal but also have unit length (i.e., they
-are normalized). Orthonormal matrices simplify calculations in various applications, as they do not change the length of the vectors they transform.
-
-### Applications
-
-Orthogonal and orthonormal matrices play a significant role in computer graphics, as they are used to represent transformations that maintain
-the geometry of 3D objects. For instance, they can be employed to create rotation matrices, which are essential for rotating objects in 3D space.
-
-Here's a BlitzMax example demonstrating the use of an orthonormal matrix to represent a 3D rotation:
-
-```blitzmax
-Local angleX:Float = 30 ' in degrees
-Local angleY:Float = 45 ' in degrees
-Local angleZ:Float = 60 ' in degrees
-
-' Create orthonormal rotation matrices for each axis
-Local rotationMatrixX:SMat3D = SMat3D.RotationX(angleX)
-Local rotationMatrixY:SMat3D = SMat3D.RotationY(angleY)
-Local rotationMatrixZ:SMat3D = SMat3D.RotationZ(angleZ)
-
-' Combine the rotations
-Local rotationMatrix:SMat3D = rotationMatrixX * rotationMatrixY * rotationMatrixZ
-
-' Apply the rotation to a 3D vector
-Local vec:SVec3D = New SVec3D(1, 2, 3)
-Local rotatedVec:SVec3D = rotationMatrix.Apply(vec)
-```
-
-When the rotation matrix is applied to a 3D vector, it will rotate the vector according to the specified angles.
-The order in which the rotations are combined (in this example, X, then Y, then Z) will determine the final rotation.

+ 0 - 2962
matrix.mod/matrix.bmx

@@ -1,2962 +0,0 @@
-' Copyright (c) 2019-2020 Bruce A Henderson
-'
-' This software is provided 'as-is', without any express or implied
-' warranty. In no event will the authors be held liable for any damages
-' arising from the use of this software.
-' 
-' Permission is granted to anyone to use this software for any purpose,
-' including commercial applications, and to alter it and redistribute it
-' freely, subject to the following restrictions:
-' 
-'    1. The origin of this software must not be misrepresented; you must not
-'    claim that you wrote the original software. If you use this software
-'    in a product, an acknowledgment in the product documentation would be
-'    appreciated but is not required.
-' 
-'    2. Altered source versions must be plainly marked as such, and must not be
-'    misrepresented as being the original software.
-' 
-'    3. This notice may not be removed or altered from any source
-'    distribution.
-' 
-SuperStrict
-
-Rem
-bbdoc: Math/Matrix
-End Rem
-Module BRL.Matrix
-
-ModuleInfo "Version: 1.00"
-ModuleInfo "Author: Bruce A Henderson"
-ModuleInfo "License: zlib"
-ModuleInfo "Copyright: 2019-2020 Bruce A Henderson"
-
-ModuleInfo "History: 1.00"
-ModuleInfo "History: Initial Release"
-
-Import BRL.Math
-Import BRL.Vector
-Import BRL.StringBuilder
-
-Rem
-bbdoc: A 2x2 Matrix
-End Rem
-Struct SMat2D
-	Field ReadOnly a:Double
-	Field ReadOnly b:Double
-	Field ReadOnly c:Double
-	Field ReadOnly d:Double
-	
-	Rem
-	bbdoc: Creates a new #SMat2D from the supplied arguments.
-	End Rem
-	Method New(a:Double, b:Double, c:Double, d:Double)
-		Self.a = a
-		Self.b = b
-		Self.c = c
-		Self.d = d
-	End Method
-	
-	Rem
-	bbdoc: Applies the matrix to the vector @v, returning a new vector.
-	End Rem
-	Method Apply:SVec2D(v:SVec2D)
-		Return New SVec2D(a * v.x + c * v.y, b * v.x + d * v.y)
-	End Method
-
-	Rem
-	bbdoc: Returns the identity matrix.
-	End Rem
-	Function Identity:SMat2D()
-		Return New SMat2D(1, 0, 0, 1)
-	End Function
-	
-	Rem
-	bbdoc: Adds @z to the matrix, returning a new matrix.
-	End Rem
-	Method Operator+:SMat2D(z:SMat2D)
-		Return New SMat2D(a + z.a, b + z.b, c + z.c, d + z.d)
-	End Method
-	
-	Rem
-	bbdoc: Subtracts @z from the matrix, returning a new matrix.
-	End Rem
-	Method Operator-:SMat2D(z:SMat2D)
-		Return New SMat2D(a - z.a, b - z.b, c - z.c, d - z.d)
-	End Method
-	
-	Rem
-	bbdoc: Multiplies the matrix by @z, the dot product, returning a new matrix.
-	End Rem
-	Method Operator*:SMat2D(z:SMat2D)
-		Return New SMat2D(a * z.a + c * z.b, b * z.a + d * z.b, a * z.c + c * z.d, b * z.c + d * z.d)
-	End Method
-	
-	Rem
-	bbdoc: Returns the transposition of the cofactor matrix.
-	End Rem
-	Method Adjoint:SMat2D()
-		Return New SMat2D(d, -b, -c, a)
-	End Method
-	
-	Rem
-	bbdoc: Multiplies the matrix by @z by its components, or element-wise matrix multiplication, return a new matrix.
-	End Rem
-	Method CompMul:SMat2D(z:SMat2D)
-		Return New SMat2D(a * z.a, b * z.b, c * z.c, d * z.d)
-	End Method
-	
-	Rem
-	bbdoc: Returns the determinant of the matrix.
-	End Rem
-	Method Determinant:Double()
-		Return a * d - c * b
-	End Method
-	
-	Rem
-	bbdoc: Returns the inverse of the matrix.
-	End Rem
-	Method Invert:SMat2D()
-		Local det:Double = a * d - c * b
-		If det = 0 Then
-			Return New SMat2D(0, 0, 0, 0)
-		End If
-		det = 1 / det
-		Return New SMat2D(d * det, -b * det, -c * det, a * det)
-	End Method
-	
-	Rem
-	bbdoc: Rotates the matrix by @angle degrees, returning the rotated matrix.
-	End Rem
-	Method Rotate:SMat2D(angle:Double)
-		Local sa:Double = Sin(angle)
-		Local ca:Double = Cos(angle)
-		Return New SMat2D(a * ca + c * sa, b * ca + d * sa, a * -sa + c * ca, b * -sa + d * ca)
-	End Method
-	
-	Rem
-	bbdoc: Creates a rotated matrix of @angle degrees.
-	End Rem
-	Function Rotation:SMat2D(angle:Double)
-		Local sa:Double = Sin(angle)
-		Local ca:Double = Cos(angle)
-		Return New SMat2D(ca, sa, -sa, ca)
-	End Function
-	
-	Rem
-	bbdoc: Returns the scale of this matrix.
-	End Rem
-	Method Scale:SMat2D(s:SVec2D)
-		Return New SMat2D(a * s.x, b * s.x, c * s.y, d * s.y)
-	End Method
-	
-	Rem
-	bbdoc: Creates a scaled matrix of the scale @s.
-	End Rem
-	Function Scaling:SMat2D(s:SVec2D)
-		Return New SMat2D(s.x, 0, 0, s.y)
-	End Function
-	
-	Rem
-	bbdoc: Returns the transpose of this matrix.
-	End Rem
-	Method Transpose:SMat2D()
-		Return New SMat2D(a, c, b, d)
-	End Method
-	
-	Rem
-	bbdoc: Returns a #String representation of the matrix.
-	End Rem
-	Method ToString:String() Override
-		Local sb:TStringBuilder = New TStringBuilder
-		
-		sb.Append(a).Append(", ").Append(c).Append(",~n")
-		sb.Append(b).Append(", ").Append(d)
-		
-		Return sb.ToString()
-	End Method
-
-End Struct
-
-Rem
-bbdoc: A 3x3 matrix.
-End Rem
-Struct SMat3D
-	Field ReadOnly a:Double
-	Field ReadOnly b:Double
-	Field ReadOnly c:Double
-	Field ReadOnly d:Double
-	Field ReadOnly e:Double
-	Field ReadOnly f:Double
-	Field ReadOnly g:Double
-	Field ReadOnly h:Double
-	Field ReadOnly i:Double
-
-	Rem
-	bbdoc: Creates a new #SMat3D from the supplied arguments.
-	End Rem
-	Method New(a:Double, b:Double, c:Double, d:Double, e:Double, f:Double, g:Double, h:Double, i:Double)
-		Self.a = a
-		Self.b = b
-		Self.c = c
-		Self.d = d
-		Self.e = e
-		Self.f = f
-		Self.g = g
-		Self.h = h
-		Self.i = i
-	End Method
-
-	Rem
-	bbdoc: Applies the matrix to the vector @v, returning a new vector.
-	End Rem
-	Method Apply:SVec2D(v:SVec2D)
-		Return New SVec2D(a * v.x + d * v.y + g, b * v.x + e * v.y + h)
-	End Method
-
-	Rem
-	bbdoc: Applies the matrix to the vector @v, returning a new vector.
-	End Rem
-	Method Apply:SVec3D(v:SVec3D)
-		Return New SVec3D(v.x * a + v.y * d + v.z * g, v.x * b + v.y * e + v.z * h, v.x * c + v.y * f + v.z * i)
-	End Method
-
-	Rem
-	bbdoc: Applies the matrix to the vector @v, returning a new vector.
-	End Rem
-	Method Apply:SVec4D(v:SVec4D)
-		Return New SVec4D(v.x * a + v.y * d + v.z * g, v.x * b + v.y * e + v.z * h, v.x * c + v.y * f + v.z * i, 0)
-	End Method
-
-	Rem
-	bbdoc: Return the 3x3 identity matrix.
-	End Rem
-	Function Identity:SMat3D()
-		Return New SMat3D(1, 0, 0, 0, 1, 0, 0, 0, 1)
-	End Function
-	
-	Rem
-	bbdoc: Adds @z to the matrix, returning a new matrix.
-	End Rem
-	Method Operator+:SMat3D(z:SMat3D Var)
-		Return New SMat3D(a + z.a, b + z.b, c + z.c, d + z.d, e + z.e, f + z.f, g + z.g, h + z.h, i + z.i)
-	End Method
-	
-	Rem
-	bbdoc: Subtracts @z from the matrix, returning a new matrix.
-	End Rem
-	Method Operator-:SMat3D(z:SMat3D Var)
-		Return New SMat3D(a - z.a, b - z.b, c - z.c, d - z.d, e - z.e, f - z.f, g - z.g, h - z.h, i - z.i)
-	End Method
-	
-	Rem
-	bbdoc: Multiplies the matrix by @z, the dot product, returning a new matrix.
-	End Rem
-	Method Operator*:SMat3D(z:SMat3D Var)
-		Local a00:Double = a
-		Local a01:Double = b
-		Local a02:Double = c
-		Local a10:Double = d
-		Local a11:Double = e
-		Local a12:Double = f
-		Local a20:Double = g
-		Local a21:Double = h
-		Local a22:Double = i
-		Local b00:Double = z.a
-		Local b01:Double = z.b
-		Local b02:Double = z.c
-		Local b10:Double = z.d
-		Local b11:Double = z.e
-		Local b12:Double = z.f
-		Local b20:Double = z.g
-		Local b21:Double = z.h
-		Local b22:Double = z.i
-		Return New SMat3D(b00 * a00 + b01 * a10 + b02 * a20, ..
-			b00 * a01 + b01 * a11 + b02 * a21, ..
-			b00 * a02 + b01 * a12 + b02 * a22, ..
-			b10 * a00 + b11 * a10 + b12 * a20, ..
-			b10 * a01 + b11 * a11 + b12 * a21, ..
-			b10 * a02 + b11 * a12 + b12 * a22, ..
-			b20 * a00 + b21 * a10 + b22 * a20, ..
-			b20 * a01 + b21 * a11 + b22 * a21, ..
-			b20 * a02 + b21 * a12 + b22 * a22)
-	End Method
-	
-	Rem
-	bbdoc: Returns the transposition of the cofactor matrix.
-	End Rem
-	Method Adjoint:SMat3D()
-		Return New SMat3D(e * i - f * h, ..
-			c * h - b * i, ..
-			b * f - c * e, ..
-			f * g - d * i, ..
-			a * i - c * g, ..
-			c * d - a * f, ..
-			d * h - e * g, ..
-			b * g - a * h, ..
-			a * e - b * d)
-	End Method
-	
-	Rem
-	bbdoc: Multiplies the matrix by @z by its components, or element-wise matrix multiplication, return a new matrix.
-	End Rem
-	Method CompMul:SMat3D(z:SMat3D Var)
-		Return New SMat3D(a * z.a, b * z.b, c * z.c, d * z.d, e * z.e, f * z.f, g * z.g, h * z.h, i * z.i)
-	End Method
-	
-	Rem
-	bbdoc: Returns the determinant of the matrix.
-	End Rem
-	Method Determinant:Double()
-		Local a00:Double = a
-		Local a01:Double = b
-		Local a02:Double = c
-		Local a10:Double = d
-		Local a11:Double = e
-		Local a12:Double = f
-		Local a20:Double = g
-		Local a21:Double = h
-		Local a22:Double = i
-		Return a00 * ( a22 * a11 - a12 * a21) + a01 * (-a22 * a10 + a12 * a20) + a02 * ( a21 * a10 - a11 * a20)
-	End Method
-	
-	Rem
-	bbdoc: Returns the inverse of the matrix.
-	End Rem
-	Method Invert:SMat3D()
-		Local a00:Double = a
-		Local a01:Double = b
-		Local a02:Double = c
-		Local a10:Double = d
-		Local a11:Double = e
-		Local a12:Double = f
-		Local a20:Double = g
-		Local a21:Double = h
-		Local a22:Double = i
-		Local b01:Double =  a22 * a11 - a12 * a21
-		Local b11:Double = -a22 * a10 + a12 * a20
-		Local b21:Double =  a21 * a10 - a11 * a20
-		Local det:Double = a00 * b01 + a01 * b11 + a02 * b21
-		If det = 0 Then
-			Return New SMat3D(0, 0, 0, 0, 0, 0, 0, 0, 0)
-		End If
-		det = 1 / det
-		Return New SMat3D(b01 * det, ..
-			(-a22 * a01 + a02 * a21) * det, ..
-			( a12 * a01 - a02 * a11) * det,
-			b11 * det, ..
-			( a22 * a00 - a02 * a20) * det, ..
-			(-a12 * a00 + a02 * a10) * det, ..
-			b21 * det, ..
-			(-a21 * a00 + a01 * a20) * det, ..
-			( a11 * a00 - a01 * a10) * det)
-	End Method
-	
-	Rem
-	bbdoc: Rotates the matrix on a 2D rotation in the XY plane by @angle degrees, returning a new matrix.
-	End Rem
-	Method Rotate:SMat3D(angle:Double)
-		Local sa:Double = Sin(angle)
-		Local ca:Double = Cos(angle)
-		Return New SMat3D(ca * a + sa * d, ..
-			ca * b + sa * e, ..
-			ca * c + sa * f, ..
-			ca * d - sa * a, ..
-			ca * e - sa * b, ..
-			ca * f - sa * c, ..
-			g, h, i)
-	End Method
-
-	Rem
-	bbdoc: Rotates the matrix around the Z axis by @angle degrees, returning a new matrix.
-	End Rem
-	Method RotateZ:SMat3D(angle:Double)
-		Local ca:Double = Cos(angle)
-		Local sa:Double = Sin(angle)
-	
-		Return New SMat3D( ..
-			a * ca - c * sa, ..
-			b * ca - d * sa, ..
-			0, ..
-			a * sa + c * ca, ..
-			b * sa + d * ca, ..
-			0, ..
-			g, h, i)
-	End Method
-
-	Rem
-	bbdoc: Returns a rotation matrix of @angle degrees.
-	End Rem
-	Function Rotation:SMat3D(angle:Double)
-		Local sa:Double = Sin(angle)
-		Local ca:Double = Cos(angle)
-		Return New SMat3D(ca, sa, 0, -sa, ca, 0, 0, 0, 1)
-	End Function
-	
-	Rem
-	bbdoc: Scales the matrix by @s, returning a new matrix.
-	End Rem
-	Method Scale:SMat3D(s:SVec2D)
-		Local bx:Double = s.x
-		Local by:Double = s.y
-		Return New SMat3D(a * bx, b * bx, c * bx, d * by, e * by, f * by, g, h, i)
-	End Method
-	
-	Rem
-	bbdoc: Returns a scaling matrix of @s.
-	End Rem
-	Function Scaling:SMat3D(s:SVec2D)
-		Return New SMat3D(s.x, 0, 0, 0, s.y, 0, 0, 0, 1)
-	End Function
-
-	Rem
-	bbdoc: Returns a translation with the specified @x, @y, and @z displacements.
-	End Rem
-	Method Translate:SMat3D(x:Double, y:Double, z:Double)
-		Return New SMat3D( ..
-			a, b, c, ..
-			d, e, f, ..
-			g + a * x + b * y + c * z, ..
-			h + d * x + e * y + f * z, ..
-			i + g * x + h * y + i * z)
-	End Method
-
-	Rem
-	bbdoc: Returns a translation with displacement vector @s.
-	End Rem
-	Method Translate:SMat3D(t:SVec3D)
-		Return New SMat3D( ..
-			a, b, c, ..
-			d, e, f, ..
-			g + a * t.x + b * t.y + c * t.z, ..
-			h + d * t.x + e * t.y + f * t.z, ..
-			i + g * t.x + h * t.y + i * t.z)
-	End Method
-	
-	Rem
-	bbdoc: Returns a transposition of the matrix.
-	End Rem
-	Method Transpose:SMat3D()
-		Return New SMat3D(a, d, g, b, e, h, c, f, i)
-	End Method
-	
-	Rem
-	bbdoc: Returns a #String representation of the matrix.
-	End Rem
-	Method ToString:String() Override
-		Local sb:TStringBuilder = New TStringBuilder
-		
-		sb.Append(a).Append(", ").Append(d).Append(", ").Append(g).Append(",~n")
-		sb.Append(b).Append(", ").Append(e).Append(", ").Append(h).Append(",~n")
-		sb.Append(c).Append(", ").Append(f).Append(", ").Append(i)
-		
-		Return sb.ToString()
-	End Method
-	
-End Struct
-
-Rem
-bbdoc: A standard 4x4 transformation matrix.
-End Rem
-Struct SMat4D
-	Field ReadOnly a:Double
-	Field ReadOnly b:Double
-	Field ReadOnly c:Double
-	Field ReadOnly d:Double
-	Field ReadOnly e:Double
-	Field ReadOnly f:Double
-	Field ReadOnly g:Double
-	Field ReadOnly h:Double
-	Field ReadOnly i:Double
-	Field ReadOnly j:Double
-	Field ReadOnly k:Double
-	Field ReadOnly l:Double
-	Field ReadOnly m:Double
-	Field ReadOnly n:Double
-	Field ReadOnly o:Double
-	Field ReadOnly p:Double
-
-	Rem
-	bbdoc: Creates a new #SMat4D from the supplied arguments.
-	End Rem
-	Method New(a:Double, b:Double, c:Double, d:Double, e:Double, f:Double, g:Double, h:Double, i:Double, j:Double, k:Double, l:Double, m:Double, n:Double, o:Double, p:Double)
-		Self.a = a
-		Self.b = b
-		Self.c = c
-		Self.d = d
-		Self.e = e
-		Self.f = f
-		Self.g = g
-		Self.h = h
-		Self.i = i
-		Self.j = j
-		Self.k = k
-		Self.l = l
-		Self.m = m
-		Self.n = n
-		Self.o = o
-		Self.p = p
-	End Method
-
-	Rem
-	bbdoc: Applies the matrix to the vector @v, returning a new vector.
-	End Rem
-	Method Apply:SVec2D(v:SVec2D)
-		Return New SVec2D(a * v.x + e * v.y + m, b * v.x + f * v.y + n)
-	End Method
-
-	Rem
-	bbdoc: Applies the 4x4 matrix @b to the vector, returning a new vector.
-	End Rem
-	Method Apply:SVec3D(v:SVec3D)
-		Local w:Double = d * v.x + h * v.y + l * v.z + p
-		If w = 0 Then
-			w = 1
-		Else
-			w = 1 / w
-		End If
-		Return New SVec3D((a * v.x + e * v.y + i * v.z + m) * w, ..
-			(b * v.x + f * v.y + j * v.z + n) * w, ..
-			(c * v.x + g * v.y + k * v.z + o) * w)
-	End Method
-
-	Rem
-	bbdoc: Applies the 4x4 matrix @b to the vector, returning a new vector.
-	End Rem
-	Method Apply:SVec4D(v:SVec4D)
-		Return New SVec4D(a * v.x + e * v.y + i * v.z + m * v.w, ..
-			b * v.x + f * v.y + j * v.z + n * v.w, ..
-			c * v.x + g * v.y + k * v.z + o * v.w, ..
-			d * v.x + h * v.y + l * v.z + p * v.w)
-	End Method
-
-	Rem
-	bbdoc: Returns the identity matrix.
-	End Rem
-	Function Identity:SMat4D()
-		Return New SMat4D(1, 0, 0, 0, ..
-				0, 1, 0, 0, ..
-				0, 0, 1, 0, ..
-				0, 0, 0, 1)
-	End Function
-	
-	Rem
-	bbdoc: Adds @z to the matrix, returning a new matrix.
-	End Rem
-	Method Operator+:SMat4D(z:SMat4D Var)
-		Return New SMat4D(a + z.a, b + z.b, c + z.c, d + z.d, ..
-			e + z.e, f + z.f, g + z.g, h + z.h, ..
-			i + z.i, j + z.j, k + z.k, l + z.l, ..
-			m + z.m, n + z.n, o + z.o, p + z.p)
-	End Method
-	
-	Rem
-	bbdoc: Subtracts @z from the matrix, returning a new matrix.
-	End Rem
-	Method Operator-:SMat4D(z:SMat4D Var)
-		Return New SMat4D(a - z.a, b - z.b, c - z.c, d - z.d, ..
-			e - z.e, f - z.f, g - z.g, h - z.h, ..
-			i - z.i, j - z.j, k - z.k, l - z.l, ..
-			m - z.m, n - z.n, o - z.o, p - z.p)
-	End Method
-
-	Rem
-	bbdoc: Multiplies the matrix by @z, the dot product, returning a new matrix. 
-	End Rem
-	Method Operator*:SMat4D(z:SMat4D Var)
-		Local a00:Double = a
-		Local a01:Double = b
-		Local a02:Double = c
-		Local a03:Double = d
-		Local a10:Double = e
-		Local a11:Double = f
-		Local a12:Double = g
-		Local a13:Double = h
-		Local a20:Double = i
-		Local a21:Double = j
-		Local a22:Double = k
-		Local a23:Double = l
-		Local a30:Double = m
-		Local a31:Double = n
-		Local a32:Double = o
-		Local a33:Double = p
-		Local b00:Double = z.a
-		Local b01:Double = z.b
-		Local b02:Double = z.c
-		Local b03:Double = z.d
-		Local b10:Double = z.e
-		Local b11:Double = z.f
-		Local b12:Double = z.g
-		Local b13:Double = z.h
-		Local b20:Double = z.i
-		Local b21:Double = z.j
-		Local b22:Double = z.k
-		Local b23:Double = z.l
-		Local b30:Double = z.m
-		Local b31:Double = z.n
-		Local b32:Double = z.o
-		Local b33:Double = z.p
-		Return New SMat4D(b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30, ..
-			b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31, ..
-			b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32, ..
-			b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33, ..
-			b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30, ..
-			b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31, ..
-			b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32, ..
-			b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33, ..
-			b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30, ..
-			b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31, ..
-			b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32, ..
-			b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33, ..
-			b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30, ..
-			b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31, ..
-			b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32, ..
-			b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33)
-	End Method
-	
-	Rem
-	bbdoc: Returns the transposition of the cofactor matrix.
-	End Rem
-	Method Adjoint:SMat4D()
-		Local a00:Double = a
-		Local a01:Double = b
-		Local a02:Double = c
-		Local a03:Double = d
-		Local a10:Double = e
-		Local a11:Double = f
-		Local a12:Double = g
-		Local a13:Double = h
-		Local a20:Double = i
-		Local a21:Double = j
-		Local a22:Double = k
-		Local a23:Double = l
-		Local a30:Double = m
-		Local a31:Double = n
-		Local a32:Double = o
-		Local a33:Double = p
-		Return New SMat4D(a11 * (a22 * a33 - a23 * a32) - a21 * (a12 * a33 - a13 * a32) + a31 * (a12 * a23 - a13 * a22), ..
-			-(a01 * (a22 * a33 - a23 * a32) - a21 * (a02 * a33 - a03 * a32) + a31 * (a02 * a23 - a03 * a22)), ..
-			a01 * (a12 * a33 - a13 * a32) - a11 * (a02 * a33 - a03 * a32) + a31 * (a02 * a13 - a03 * a12), ..
-			-(a01 * (a12 * a23 - a13 * a22) - a11 * (a02 * a23 - a03 * a22) + a21 * (a02 * a13 - a03 * a12)), ..
-			-(a10 * (a22 * a33 - a23 * a32) - a20 * (a12 * a33 - a13 * a32) + a30 * (a12 * a23 - a13 * a22)), ..
-			a00 * (a22 * a33 - a23 * a32) - a20 * (a02 * a33 - a03 * a32) + a30 * (a02 * a23 - a03 * a22), ..
-			-(a00 * (a12 * a33 - a13 * a32) - a10 * (a02 * a33 - a03 * a32) + a30 * (a02 * a13 - a03 * a12)), ..
-			a00 * (a12 * a23 - a13 * a22) - a10 * (a02 * a23 - a03 * a22) + a20 * (a02 * a13 - a03 * a12), ..
-			a10 * (a21 * a33 - a23 * a31) - a20 * (a11 * a33 - a13 * a31) + a30 * (a11 * a23 - a13 * a21), ..
-			-(a00 * (a21 * a33 - a23 * a31) - a20 * (a01 * a33 - a03 * a31) + a30 * (a01 * a23 - a03 * a21)), ..
-			a00 * (a11 * a33 - a13 * a31) - a10 * (a01 * a33 - a03 * a31) + a30 * (a01 * a13 - a03 * a11), ..
-			-(a00 * (a11 * a23 - a13 * a21) - a10 * (a01 * a23 - a03 * a21) + a20 * (a01 * a13 - a03 * a11)), ..
-			-(a10 * (a21 * a32 - a22 * a31) - a20 * (a11 * a32 - a12 * a31) + a30 * (a11 * a22 - a12 * a21)), ..
-			a00 * (a21 * a32 - a22 * a31) - a20 * (a01 * a32 - a02 * a31) + a30 * (a01 * a22 - a02 * a21), ..
-			-(a00 * (a11 * a32 - a12 * a31) - a10 * (a01 * a32 - a02 * a31) + a30 * (a01 * a12 - a02 * a11)), ..
-			a00 * (a11 * a22 - a12 * a21) - a10 * (a01 * a22 - a02 * a21) + a20 * (a01 * a12 - a02 * a11))
-	End Method
-	
-	Rem
-	bbdoc: Multiplies the matrix by @z by its components, or element-wise matrix multiplication, returning a new matrix.
-	End Rem
-	Method CompMul:SMat4D(z:SMat4D Var)
-		Return New SMat4D(a * z.a, b * z.b, c * z.c, d * z.d, ..
-			e * z.e, f * z.f, g * z.g, h * z.h, ..
-			i * z.i, j * z.j, k * z.k, l * z.l, ..
-			m * z.m, n * z.n, o * z.o, p * z.p)
-	End Method
-	
-	Rem
-	bbdoc: Returns the determinant of the matrix.
-	End Rem
-	Method Determinant:Double()
-		Local a00:Double = a
-		Local a01:Double = b
-		Local a02:Double = c
-		Local a03:Double = d
-		Local a10:Double = e
-		Local a11:Double = f
-		Local a12:Double = g
-		Local a13:Double = h
-		Local a20:Double = i
-		Local a21:Double = j
-		Local a22:Double = k
-		Local a23:Double = l
-		Local a30:Double = m
-		Local a31:Double = n
-		Local a32:Double = o
-		Local a33:Double = p
-		Local b00:Double = a00 * a11 - a01 * a10
-		Local b01:Double = a00 * a12 - a02 * a10
-		Local b02:Double = a00 * a13 - a03 * a10
-		Local b03:Double = a01 * a12 - a02 * a11
-		Local b04:Double = a01 * a13 - a03 * a11
-		Local b05:Double = a02 * a13 - a03 * a12
-		Local b06:Double = a20 * a31 - a21 * a30
-		Local b07:Double = a20 * a32 - a22 * a30
-		Local b08:Double = a20 * a33 - a23 * a30
-		Local b09:Double = a21 * a32 - a22 * a31
-		Local b10:Double = a21 * a33 - a23 * a31
-		Local b11:Double = a22 * a33 - a23 * a32
-		Return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06
-	End Method
-
-	Rem
-	bbdoc: Returns a projection matrix with a viewing frustum defined by the plane coordinates passed in.
-	End Rem
-	Function Frustum:SMat4D(l:Double, r:Double, b:Double, t:Double, n:Double, f:Double)
-		Local rl:Double = 1.0 / (r - l)
-		Local tb:Double = 1.0 / (t - b)
-		Local nf:Double = 1.0 / (n - f)
-		Return New SMat4D((2.0 * n) * rl, 0, 0, 0, ..
-			0, (2.0 * n) * tb, 0, 0, ..
-			(r + l) * rl, (t + b) * tb, (f + n) * nf, -1, ..
-			0, 0, (2.0 * n * f) * nf, 0)
-	End Function
-	
-	Rem
-	bbdoc: The inverse of this matrix.
-	about: An inverted matrix is such that if multiplied by the original would result in identity matrix.
-	If some matrix transforms vectors in a particular way, then the inverse matrix can transform them back.
-	End Rem
-	Method Invert:SMat4D()
-		Local a00:Double = a
-		Local a01:Double = b
-		Local a02:Double = c
-		Local a03:Double = d
-		Local a10:Double = e
-		Local a11:Double = f
-		Local a12:Double = g
-		Local a13:Double = h
-		Local a20:Double = i
-		Local a21:Double = j
-		Local a22:Double = k
-		Local a23:Double = l
-		Local a30:Double = m
-		Local a31:Double = n
-		Local a32:Double = o
-		Local a33:Double = p
-		Local b00:Double = a00 * a11 - a01 * a10
-		Local b01:Double = a00 * a12 - a02 * a10
-		Local b02:Double = a00 * a13 - a03 * a10
-		Local b03:Double = a01 * a12 - a02 * a11
-		Local b04:Double = a01 * a13 - a03 * a11
-		Local b05:Double = a02 * a13 - a03 * a12
-		Local b06:Double = a20 * a31 - a21 * a30
-		Local b07:Double = a20 * a32 - a22 * a30
-		Local b08:Double = a20 * a33 - a23 * a30
-		Local b09:Double = a21 * a32 - a22 * a31
-		Local b10:Double = a21 * a33 - a23 * a31
-		Local b11:Double = a22 * a33 - a23 * a32
-		Local det:Double = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06
-		If det = 0 Then
-			Return New SMat4D(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
-		End If
-		det = 1 / det
-		Return New SMat4D((a11 * b11 - a12 * b10 + a13 * b09) * det, ..
-			(a02 * b10 - a01 * b11 - a03 * b09) * det, ..
-			(a31 * b05 - a32 * b04 + a33 * b03) * det, ..
-			(a22 * b04 - a21 * b05 - a23 * b03) * det, ..
-			(a12 * b08 - a10 * b11 - a13 * b07) * det, ..
-			(a00 * b11 - a02 * b08 + a03 * b07) * det, ..
-			(a32 * b02 - a30 * b05 - a33 * b01) * det, ..
-			(a20 * b05 - a22 * b02 + a23 * b01) * det, ..
-			(a10 * b10 - a11 * b08 + a13 * b06) * det, ..
-			(a01 * b08 - a00 * b10 - a03 * b06) * det, ..
-			(a30 * b04 - a31 * b02 + a33 * b00) * det, ..
-			(a21 * b02 - a20 * b04 - a23 * b00) * det, ..
-			(a11 * b07 - a10 * b09 - a12 * b06) * det, ..
-			(a00 * b09 - a01 * b07 + a02 * b06) * det, ..
-			(a31 * b01 - a30 * b03 - a32 * b00) * det, ..
-			(a20 * b03 - a21 * b01 + a22 * b00) * det)
-	End Method
-	
-	Rem
-	bbdoc: Computes a transformation matrix that corresponds to a camera viewing the @eye from the @pos.
-	about: The right-hand vector is perpendicular to the up vector.
-	End Rem
-	Function LookAt:SMat4D(eye:SVec3D, pos:SVec3D, upDir:SVec3D)
-		Local forward:SVec3D = (eye - pos).Normal()
-		Local lft:SVec3D = upDir.Cross(forward).Normal()
-		
-		Local up:SVec3D = forward.Cross(lft)
-		
-		Local mat:SMat4D = SMat4D.Identity()
-		
-		Local a00:Double = lft.x
-		Local a01:Double = up.x
-		Local a02:Double = forward.x
-		Local a03:Double = mat.d
-		Local a10:Double = lft.y
-		Local a11:Double = up.y
-		Local a12:Double = forward.y
-		Local a13:Double = mat.h
-		Local a20:Double = lft.z
-		Local a21:Double = up.z
-		Local a22:Double = forward.z
-		Local a23:Double = mat.l
-		Local a30:Double = -lft.x * eye.x - lft.y * eye.y - lft.z * eye.z
-		Local a31:Double = -up.x * eye.x - up.y * eye.y - up.z * eye.z
-		Local a32:Double = -forward.x * eye.x - forward.y * eye.y - forward.z * eye.z
-		Local a33:Double = mat.p
-
-		Return New SMat4D(a00, a01, a02, a03, a10, a11, a12, a13, a20, a21, a22, a23, a30, a31, a32, a33)
-	End Function
-	
-	Rem
-	bbdoc: Creates an orthogonal projection matrix.
-	about: The returned matrix, when used as a Camera's projection matrix, creates a view showing the area between @width and @height, with @zNear and @zFar as the near and far depth clipping planes.
-	End Rem
-	Function Orthogonal:SMat4D(width:Double, height:Double, zNear:Double, zFar:Double)
-		Local nf:Double = 1.0 / (zNear - zFar)
-		Return New SMat4D(2.0 / width, 0, 0, 0, ..
-			0, 2.0 / height, 0, 0, ..
-			0, 0, 2.0 * nf, 0, ..
-			0, 0, (zNear + zFar) * nf, 1)
-	End Function
-	
-	Rem
-	bbdoc: Creates a perspective projection matrix.
-	End Rem
-	Function Perspective:SMat4D(fov:Double, w:Double, h:Double, n:Double, f:Double)
-		Local tf:Double = Tan(fov / 2)
-		Return New SMat4D(1 / ((w / h) * tf), 0, 0, 0, ..
-			0, 1 / tf, 0, 0, ..
-			0, 0, - (f + n) / (f - n), -1, ..
-			0, 0, - (2 * f * n) / (f - n), 0)
-	End Function
-	
-	Rem
-	bbdoc: Creates a rotation matrix, rotated @angle degrees around the point @axis.
-	End Rem
-	Method Rotate:SMat4D(axis:SVec3D, angle:Double)
-		Local c:Double = Cos(angle)
-		Local ic:Double = 1 - c
-		Local s:Double = Sin(angle)
-
-		Local norm:SVec3D = axis.Normal()
-
-		Local x:Double = ic * norm.x
-		Local y:Double = ic * norm.y
-		Local z:Double = ic * norm.z
-		Local mat:SMat4D = New SMat4D(c + x * norm.x, x * norm.y + s * norm.z, x * norm.z - s * norm.y, 0, ..
-				y * norm.x - s * norm.z, c + y * norm.y, y * norm.z + s * norm.x, 0, ..
-				z * norm.x + s * norm.y, z * norm.y - s * norm.x, c + z * norm.z, 0, ..
-				0, 0, 0, 1)
-		
-		Return Self * mat
-	End Method
-	
-	Rem
-	bbdoc: Returns a rotation matrix on the given @axis and @angle degrees.
-	End Rem
-	Function Rotation:SMat4D(axis:SVec3D, angle:Double)
-		Local x:Double = axis.x
-		Local y:Double = axis.y
-		Local z:Double = axis.z
-		Local sa:Double = Sin(angle)
-		Local ca:Double = Cos(angle)
-		Local t:Double = 1 - ca
-		Return New SMat4D(x * x * t + ca, ..
-			y * x * t + z * sa, ..
-			z * x * t - y * sa, ..
-			0, ..
-			x * y * t - z * sa, ..
-			y * y * t + ca, ..
-			z * y * t + x * sa, ..
-			0, ..
-			x * z * t + y * sa, ..
-			y * z * t - x * sa, ..
-			z * z * t + ca, ..
-			0, 0, 0, 0, 1)
-	End Function
-	
-	Rem
-	bbdoc: Scales the matrix, return the new scaled matrix.
-	End Rem
-	Method Scale:SMat4D(s:SVec3D)
-		Local bx:Double = s.x
-		Local by:Double = s.y
-		Local bz:Double = s.z
-		Return New SMat4D(a * bx, b * bx, c * bx, d * bx, ..
-			e * by, f * by, g * by, h * by, ..
-			i * bz, j * bz, k * bz, l * bz, ..
-			m, n, o, p)
-	End Method
-	
-	Rem
-	bbdoc: Creates a scaling matrix.
-	End Rem
-	Function Scaling:SMat4D(s:SVec3D)
-		Return New SMat4D(s.x, 0, 0, 0, 0, s.y, 0, 0, 0, 0, s.z, 0, 0, 0, 0, 1)
-	End Function
-
-	Rem
-	bbdoc: Returns the transpose of this matrix.
-	about: The transposed matrix is the one that has the columns exchanged with its rows.
-	End Rem
-	Method Transpose:SMat4D()
-		Return New SMat4D(a, e, i, m, b, f, j, n, c, g, k, o, d, h, l, p)
-	End Method
-	
-	Rem
-	bbdoc: Translates the matrix to @s.
-	End Rem
-	Method Translate:SMat4D(s:SVec3D)
-		Local bx:Double = s.x
-		Local by:Double = s.y
-		Local bz:Double = s.z
-		Return New SMat4D(a, b, c, d, e, f, g, h, i, j, k, l, ..
-			a * bx + e * by + i * bz + m, ..
-			b * bx + f * by + j * bz + n, ..
-			c * bx + g * by + k * bz + o, ..
-			d * bx + h * by + l * bz + p)
-	End Method
-
-	Rem
-	bbdoc: Creates a translation matrix.
-	End Rem
-	Function Translation:SMat4D(s:SVec3D)
-		Return New SMat4D(1, 0, 0, 0, ..
-			0, 1, 0, 0, ..
-			0, 0, 1, 0, ..
-			s.x, s.y, s.z, 1)
-	End Function
-	
-	Rem
-	bbdoc: Returns a #String representation of the matrix.
-	End Rem
-	Method ToString:String() Override
-		Local sb:TStringBuilder = New TStringBuilder
-		
-		sb.Append(a).Append(", ").Append(e).Append(", ").Append(i).Append(", ").Append(m).Append(",~n")
-		sb.Append(b).Append(", ").Append(f).Append(", ").Append(j).Append(", ").Append(n).Append(",~n")
-		sb.Append(c).Append(", ").Append(g).Append(", ").Append(k).Append(", ").Append(o).Append(",~n")
-		sb.Append(d).Append(", ").Append(h).Append(", ").Append(l).Append(", ").Append(p)
-		
-		Return sb.ToString()
-	End Method
-	
-End Struct
-
-Rem
-bbdoc: A #Float backed 2x2 Matrix.
-End Rem
-Struct SMat2F
-	Field ReadOnly a:Float
-	Field ReadOnly b:Float
-	Field ReadOnly c:Float
-	Field ReadOnly d:Float
-	
-	Rem
-	bbdoc: Creates a new #SMat2F from the supplied arguments.
-	End Rem
-	Method New(a:Float, b:Float, c:Float, d:Float)
-		Self.a = a
-		Self.b = b
-		Self.c = c
-		Self.d = d
-	End Method
-	
-	Rem
-	bbdoc: Applies the matrix to the vector @v, returning a new vector.
-	End Rem
-	Method Apply:SVec2F(v:SVec2F)
-		Return New SVec2F(a * v.x + c * v.y, b * v.x + d * v.y)
-	End Method
-
-	Rem
-	bbdoc: Returns the identity matrix.
-	End Rem
-	Function Identity:SMat2F()
-		Return New SMat2F(1, 0, 0, 1)
-	End Function
-	
-	Rem
-	bbdoc: Adds @z to the matrix, returning a new matrix.
-	End Rem
-	Method Operator+:SMat2F(z:SMat2F)
-		Return New SMat2F(a + z.a, b + z.b, c + z.c, d + z.d)
-	End Method
-	
-	Rem
-	bbdoc: Subtracts @z from the matrix, returning a new matrix.
-	End Rem
-	Method Operator-:SMat2F(z:SMat2F)
-		Return New SMat2F(a - z.a, b - z.b, c - z.c, d - z.d)
-	End Method
-	
-	Rem
-	bbdoc: Multiplies the matrix by @z, the dot product, returning a new matrix.
-	End Rem
-	Method Operator*:SMat2F(z:SMat2F)
-		Return New SMat2F(a * z.a + c * z.b, b * z.a + d * z.b, a * z.c + c * z.d, b * z.c + d * z.d)
-	End Method
-	
-	Rem
-	bbdoc: Returns the transposition of the cofactor matrix.
-	End Rem
-	Method Adjoint:SMat2F()
-		Return New SMat2F(d, -b, -c, a)
-	End Method
-	
-	Rem
-	bbdoc: Multiplies the matrix by @z by its components, return a new matrix.
-	End Rem
-	Method CompMul:SMat2F(z:SMat2F)
-		Return New SMat2F(a * z.a, b * z.b, c * z.c, d * z.d)
-	End Method
-	
-	Rem
-	bbdoc: Returns the determinant of the matrix.
-	End Rem
-	Method Determinant:Float()
-		Return a * d - c * b
-	End Method
-	
-	Rem
-	bbdoc: Returns the inverse of the matrix.
-	End Rem
-	Method Invert:SMat2F()
-		Local det:Float = a * d - c * b
-		If det = 0 Then
-			Return New SMat2F(0, 0, 0, 0)
-		End If
-		det = 1 / det
-		Return New SMat2F(d * det, -b * det, -c * det, a * det)
-	End Method
-	
-	Rem
-	bbdoc: Rotates the matrix by @angle degrees, returning the rotated matrix.
-	End Rem
-	Method Rotate:SMat2F(angle:Double)
-		Local sa:Double = Sin(angle)
-		Local ca:Double = Cos(angle)
-		Return New SMat2F(Float(a * ca + c * sa), Float(b * ca + d * sa), Float(a * -sa + c * ca), Float(b * -sa + d * ca))
-	End Method
-	
-	Rem
-	bbdoc: Creates a rotated matrix of @angle degrees.
-	End Rem
-	Function Rotation:SMat2F(angle:Double)
-		Local sa:Double = Sin(angle)
-		Local ca:Double = Cos(angle)
-		Return New SMat2F(Float(ca), Float(sa), Float(-sa), Float(ca))
-	End Function
-	
-	Rem
-	bbdoc: Returns the scale of this matrix.
-	End Rem
-	Method Scale:SMat2F(s:SVec2F)
-		Return New SMat2F(a * s.x, b * s.x, c * s.y, d * s.y)
-	End Method
-
-	Rem
-	bbdoc: Returns the scale of this matrix.
-	End Rem
-	Method Scale:SMat2F(s:SVec2D)
-		Return New SMat2F(Float(a * s.x), Float(b * s.x), Float(c * s.y), Float(d * s.y))
-	End Method
-	
-	Rem
-	bbdoc: Creates a scaled matrix of the scale @s.
-	End Rem
-	Function Scaling:SMat2F(s:SVec2F)
-		Return New SMat2F(s.x, 0, 0, s.y)
-	End Function
-
-	Rem
-	bbdoc: Creates a scaled matrix of the scale @s.
-	End Rem
-	Function Scaling:SMat2F(s:SVec2D)
-		Return New SMat2F(Float(s.x), 0, 0, Float(s.y))
-	End Function
-
-	Rem
-	bbdoc: Returns the transpose of this matrix.
-	End Rem
-	Method Transpose:SMat2F()
-		Return New SMat2F(a, c, b, d)
-	End Method
-	
-	Rem
-	bbdoc: Returns a #String representation of the matrix.
-	End Rem
-	Method ToString:String() Override
-		Local sb:TStringBuilder = New TStringBuilder
-		
-		sb.Append(a).Append(", ").Append(c).Append(",~n")
-		sb.Append(b).Append(", ").Append(d)
-		
-		Return sb.ToString()
-	End Method
-
-End Struct
-
-Rem
-bbdoc: A #Float backed 3x3 matrix.
-End Rem
-Struct SMat3F
-	Field ReadOnly a:Float
-	Field ReadOnly b:Float
-	Field ReadOnly c:Float
-	Field ReadOnly d:Float
-	Field ReadOnly e:Float
-	Field ReadOnly f:Float
-	Field ReadOnly g:Float
-	Field ReadOnly h:Float
-	Field ReadOnly i:Float
-
-	Rem
-	bbdoc: Creates a new #SMat3F from the supplied arguments.
-	End Rem
-	Method New(a:Float, b:Float, c:Float, d:Float, e:Float, f:Float, g:Float, h:Float, i:Float)
-		Self.a = a
-		Self.b = b
-		Self.c = c
-		Self.d = d
-		Self.e = e
-		Self.f = f
-		Self.g = g
-		Self.h = h
-		Self.i = i
-	End Method
-
-	Rem
-	bbdoc: Applies the matrix to the vector @v, returning a new vector.
-	End Rem
-	Method Apply:SVec2F(v:SVec2F)
-		Return New SVec2F(a * v.x + d * v.y + g, b * v.x + e * v.y + h)
-	End Method
-
-	Rem
-	bbdoc: Applies the matrix to the vector @v, returning a new vector.
-	End Rem
-	Method Apply:SVec3F(v:SVec3F)
-		Return New SVec3F(v.x * a + v.y * d + v.z * g, v.x * b + v.y * e + v.z * h, v.x * c + v.y * f + v.z * i)
-	End Method
-
-	Rem
-	bbdoc: Applies the matrix to the vector @v, returning a new vector.
-	End Rem
-	Method Apply:SVec4F(v:SVec4F)
-		Return New SVec4F(v.x * a + v.y * d + v.z * g, v.x * b + v.y * e + v.z * h, v.x * c + v.y * f + v.z * i, 0)
-	End Method
-
-	Rem
-	bbdoc: Return the 3x3 identity matrix.
-	End Rem
-	Function Identity:SMat3F()
-		Return New SMat3F(1, 0, 0, 0, 1, 0, 0, 0, 1)
-	End Function
-	
-	Rem
-	bbdoc: Adds @z to the matrix, returning a new matrix.
-	End Rem
-	Method Operator+:SMat3F(z:SMat3F Var)
-		Return New SMat3F(a + z.a, b + z.b, c + z.c, d + z.d, e + z.e, f + z.f, g + z.g, h + z.h, i + z.i)
-	End Method
-	
-	Rem
-	bbdoc: Subtracts @z from the matrix, returning a new matrix.
-	End Rem
-	Method Operator-:SMat3F(z:SMat3F Var)
-		Return New SMat3F(a - z.a, b - z.b, c - z.c, d - z.d, e - z.e, f - z.f, g - z.g, h - z.h, i - z.i)
-	End Method
-	
-	Rem
-	bbdoc: Multiplies the matrix by @z, the dot product, returning a new matrix.
-	End Rem
-	Method Operator*:SMat3F(z:SMat3F Var)
-		Local a00:Float = a
-		Local a01:Float = b
-		Local a02:Float = c
-		Local a10:Float = d
-		Local a11:Float = e
-		Local a12:Float = f
-		Local a20:Float = g
-		Local a21:Float = h
-		Local a22:Float = i
-		Local b00:Float = z.a
-		Local b01:Float = z.b
-		Local b02:Float = z.c
-		Local b10:Float = z.d
-		Local b11:Float = z.e
-		Local b12:Float = z.f
-		Local b20:Float = z.g
-		Local b21:Float = z.h
-		Local b22:Float = z.i
-		Return New SMat3F(b00 * a00 + b01 * a10 + b02 * a20, ..
-			b00 * a01 + b01 * a11 + b02 * a21, ..
-			b00 * a02 + b01 * a12 + b02 * a22, ..
-			b10 * a00 + b11 * a10 + b12 * a20, ..
-			b10 * a01 + b11 * a11 + b12 * a21, ..
-			b10 * a02 + b11 * a12 + b12 * a22, ..
-			b20 * a00 + b21 * a10 + b22 * a20, ..
-			b20 * a01 + b21 * a11 + b22 * a21, ..
-			b20 * a02 + b21 * a12 + b22 * a22)
-	End Method
-	
-	Rem
-	bbdoc: Returns the transposition of the cofactor matrix.
-	End Rem
-	Method Adjoint:SMat3F()
-		Return New SMat3F(e * i - f * h, ..
-			c * h - b * i, ..
-			b * f - c * e, ..
-			f * g - d * i, ..
-			a * i - c * g, ..
-			c * d - a * f, ..
-			d * h - e * g, ..
-			b * g - a * h, ..
-			a * e - b * d)
-	End Method
-	
-	Rem
-	bbdoc: Multiplies the matrix by @z by its components, return a new matrix.
-	End Rem
-	Method CompMul:SMat3F(z:SMat3F Var)
-		Return New SMat3F(a * z.a, b * z.b, c * z.c, d * z.d, e * z.e, f * z.f, g * z.g, h * z.h, i * z.i)
-	End Method
-	
-	Rem
-	bbdoc: Returns the determinant of the matrix.
-	End Rem
-	Method Determinant:Float()
-		Local a00:Float = a
-		Local a01:Float = b
-		Local a02:Float = c
-		Local a10:Float = d
-		Local a11:Float = e
-		Local a12:Float = f
-		Local a20:Float = g
-		Local a21:Float = h
-		Local a22:Float = i
-		Return a00 * ( a22 * a11 - a12 * a21) + a01 * (-a22 * a10 + a12 * a20) + a02 * ( a21 * a10 - a11 * a20)
-	End Method
-	
-	Rem
-	bbdoc: Returns the inverse of the matrix.
-	End Rem
-	Method Invert:SMat3F()
-		Local a00:Float = a
-		Local a01:Float = b
-		Local a02:Float = c
-		Local a10:Float = d
-		Local a11:Float = e
-		Local a12:Float = f
-		Local a20:Float = g
-		Local a21:Float = h
-		Local a22:Float = i
-		Local b01:Float =  a22 * a11 - a12 * a21
-		Local b11:Float = -a22 * a10 + a12 * a20
-		Local b21:Float =  a21 * a10 - a11 * a20
-		Local det:Float = a00 * b01 + a01 * b11 + a02 * b21
-		If det = 0 Then
-			Return New SMat3F(0, 0, 0, 0, 0, 0, 0, 0, 0)
-		End If
-		det = 1 / det
-		Return New SMat3F(b01 * det, ..
-			(-a22 * a01 + a02 * a21) * det, ..
-			( a12 * a01 - a02 * a11) * det,
-			b11 * det, ..
-			( a22 * a00 - a02 * a20) * det, ..
-			(-a12 * a00 + a02 * a10) * det, ..
-			b21 * det, ..
-			(-a21 * a00 + a01 * a20) * det, ..
-			( a11 * a00 - a01 * a10) * det)
-	End Method
-	
-	Rem
-	bbdoc: Rotates the matrix by @angle degrees, returning a new matrix.
-	End Rem
-	Method Rotate:SMat3F(angle:Double)
-		Local sa:Double = Sin(angle)
-		Local ca:Double = Cos(angle)
-		Return New SMat3F(Float(ca * a + sa * d), ..
-			Float(ca * b + sa * e), ..
-			Float(ca * c + sa * f), ..
-			Float(ca * d - sa * a), ..
-			Float(ca * e - sa * b), ..
-			Float(ca * f - sa * c), ..
-			g, h, i)
-	End Method
-	
-	Rem
-	bbdoc: Retrns a rotation matrix of @angle degrees.
-	End Rem
-	Function Rotation:SMat3F(angle:Double)
-		Local sa:Double = Sin(angle)
-		Local ca:Double = Cos(angle)
-		Return New SMat3F(Float(ca), Float(sa), 0, Float(-sa), Float(ca), 0, 0, 0, 1)
-	End Function
-	
-	Rem
-	bbdoc: Scales the matrix by @s, returning a new matrix.
-	End Rem
-	Method Scale:SMat3F(s:SVec2F)
-		Local bx:Float = s.x
-		Local by:Float = s.y
-		Return New SMat3F(a * bx, b * bx, c * bx, d * by, e * by, f * by, g, h, i)
-	End Method
-
-	Rem
-	bbdoc: Scales the matrix by @s, returning a new matrix.
-	End Rem
-	Method Scale:SMat3F(s:SVec2D)
-		Local bx:Float = s.x
-		Local by:Float = s.y
-		Return New SMat3F(Float(a * bx), Float(b * bx), Float(c * bx), Float(d * by), Float(e * by), Float(f * by), g, h, i)
-	End Method
-	
-	Rem
-	bbdoc: Returns a scaling matrix of @s.
-	End Rem
-	Function Scaling:SMat3F(s:SVec2F)
-		Return New SMat3F(s.x, 0, 0, 0, s.y, 0, 0, 0, 1)
-	End Function
-
-	Rem
-	bbdoc: Returns a scaling matrix of @s.
-	End Rem
-	Function Scaling:SMat3F(s:SVec2D)
-		Return New SMat3F(Float(s.x), 0, 0, 0, Float(s.y), 0, 0, 0, 1)
-	End Function
-
-	Rem
-	bbdoc: Returns a translation with the specified @x, @y, and @z displacements.
-	End Rem
-	Method Translate:SMat3F(x:Float, y:Float, z:Float)
-		Return New SMat3F( ..
-			a, b, c, ..
-			d, e, f, ..
-			g + a * x + b * y + c * z, ..
-			h + d * x + e * y + f * z, ..
-			i + g * x + h * y + i * z)
-	End Method
-
-	Rem
-	bbdoc: Returns a translation with displacement vector @s.
-	End Rem
-	Method Translate:SMat3F(t:SVec3F)
-		Return New SMat3F( ..
-			a, b, c, ..
-			d, e, f, ..
-			g + a * t.x + b * t.y + c * t.z, ..
-			h + d * t.x + e * t.y + f * t.z, ..
-			i + g * t.x + h * t.y + i * t.z)
-	End Method
-
-	Rem
-	bbdoc: Returns a transposition of the matrix.
-	End Rem
-	Method Transpose:SMat3F()
-		Return New SMat3F(a, d, g, b, e, h, c, f, i)
-	End Method
-	
-	Rem
-	bbdoc: Returns a #String representation of the matrix.
-	End Rem
-	Method ToString:String() Override
-		Local sb:TStringBuilder = New TStringBuilder
-		
-		sb.Append(a).Append(", ").Append(d).Append(", ").Append(g).Append(",~n")
-		sb.Append(b).Append(", ").Append(e).Append(", ").Append(h).Append(",~n")
-		sb.Append(c).Append(", ").Append(f).Append(", ").Append(i)
-		
-		Return sb.ToString()
-	End Method
-	
-End Struct
-
-Rem
-bbdoc: A standard #Float backed 4x4 transformation matrix.
-End Rem
-Struct SMat4F
-	Field ReadOnly a:Float
-	Field ReadOnly b:Float
-	Field ReadOnly c:Float
-	Field ReadOnly d:Float
-	Field ReadOnly e:Float
-	Field ReadOnly f:Float
-	Field ReadOnly g:Float
-	Field ReadOnly h:Float
-	Field ReadOnly i:Float
-	Field ReadOnly j:Float
-	Field ReadOnly k:Float
-	Field ReadOnly l:Float
-	Field ReadOnly m:Float
-	Field ReadOnly n:Float
-	Field ReadOnly o:Float
-	Field ReadOnly p:Float
-
-	Rem
-	bbdoc: Creates a new #SMat4F from the supplied arguments.
-	End Rem
-	Method New(a:Float, b:Float, c:Float, d:Float, e:Float, f:Float, g:Float, h:Float, i:Float, j:Float, k:Float, l:Float, m:Float, n:Float, o:Float, p:Float)
-		Self.a = a
-		Self.b = b
-		Self.c = c
-		Self.d = d
-		Self.e = e
-		Self.f = f
-		Self.g = g
-		Self.h = h
-		Self.i = i
-		Self.j = j
-		Self.k = k
-		Self.l = l
-		Self.m = m
-		Self.n = n
-		Self.o = o
-		Self.p = p
-	End Method
-
-	Rem
-	bbdoc: Applies the matrix to the vector @v, returning a new vector.
-	End Rem
-	Method Apply:SVec2F(v:SVec2F)
-		Return New SVec2F(a * v.x + e * v.y + m, b * v.x + f * v.y + n)
-	End Method
-
-	Rem
-	bbdoc: Applies the 4x4 matrix @b to the vector, returning a new vector.
-	End Rem
-	Method Apply:SVec3F(v:SVec3F)
-		Local w:Float = d * v.x + h * v.y + l * v.z + p
-		If w = 0 Then
-			w = 1
-		Else
-			w = 1 / w
-		End If
-		Return New SVec3F((a * v.x + e * v.y + i * v.z + m) * w, ..
-			(b * v.x + f * v.y + j * v.z + n) * w, ..
-			(c * v.x + g * v.y + k * v.z + o) * w)
-	End Method
-
-	Rem
-	bbdoc: Applies the 4x4 matrix @b to the vector, returning a new vector.
-	End Rem
-	Method Apply:SVec4F(v:SVec4F)
-		Return New SVec4F(a * v.x + e * v.y + i * v.z + m * v.w, ..
-			b * v.x + f * v.y + j * v.z + n * v.w, ..
-			c * v.x + g * v.y + k * v.z + o * v.w, ..
-			d * v.x + h * v.y + l * v.z + p * v.w)
-	End Method
-
-	Rem
-	bbdoc: Returns the identity matrix.
-	End Rem
-	Function Identity:SMat4F()
-		Return New SMat4F(1, 0, 0, 0, ..
-				0, 1, 0, 0, ..
-				0, 0, 1, 0, ..
-				0, 0, 0, 1)
-	End Function
-	
-	Rem
-	bbdoc: Adds @z to the matrix, returning a new matrix.
-	End Rem
-	Method Operator+:SMat4F(z:SMat4F Var)
-		Return New SMat4F(a + z.a, b + z.b, c + z.c, d + z.d, ..
-			e + z.e, f + z.f, g + z.g, h + z.h, ..
-			i + z.i, j + z.j, k + z.k, l + z.l, ..
-			m + z.m, n + z.n, o + z.o, p + z.p)
-	End Method
-	
-	Rem
-	bbdoc: Subtracts @z from the matrix, returning a new matrix.
-	End Rem
-	Method Operator-:SMat4F(z:SMat4F Var)
-		Return New SMat4F(a - z.a, b - z.b, c - z.c, d - z.d, ..
-			e - z.e, f - z.f, g - z.g, h - z.h, ..
-			i - z.i, j - z.j, k - z.k, l - z.l, ..
-			m - z.m, n - z.n, o - z.o, p - z.p)
-	End Method
-
-	Rem
-	bbdoc: Multiplies the matrix by @z, the dot product, returning a new matrix. 
-	End Rem
-	Method Operator*:SMat4F(z:SMat4F Var)
-		Local a00:Float = a
-		Local a01:Float = b
-		Local a02:Float = c
-		Local a03:Float = d
-		Local a10:Float = e
-		Local a11:Float = f
-		Local a12:Float = g
-		Local a13:Float = h
-		Local a20:Float = i
-		Local a21:Float = j
-		Local a22:Float = k
-		Local a23:Float = l
-		Local a30:Float = m
-		Local a31:Float = n
-		Local a32:Float = o
-		Local a33:Float = p
-		Local b00:Float = z.a
-		Local b01:Float = z.b
-		Local b02:Float = z.c
-		Local b03:Float = z.d
-		Local b10:Float = z.e
-		Local b11:Float = z.f
-		Local b12:Float = z.g
-		Local b13:Float = z.h
-		Local b20:Float = z.i
-		Local b21:Float = z.j
-		Local b22:Float = z.k
-		Local b23:Float = z.l
-		Local b30:Float = z.m
-		Local b31:Float = z.n
-		Local b32:Float = z.o
-		Local b33:Float = z.p
-		Return New SMat4F(b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30, ..
-			b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31, ..
-			b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32, ..
-			b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33, ..
-			b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30, ..
-			b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31, ..
-			b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32, ..
-			b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33, ..
-			b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30, ..
-			b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31, ..
-			b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32, ..
-			b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33, ..
-			b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30, ..
-			b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31, ..
-			b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32, ..
-			b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33)
-	End Method
-	
-	Rem
-	bbdoc: Returns the transposition of the cofactor matrix.
-	End Rem
-	Method Adjoint:SMat4F()
-		Local a00:Float = a
-		Local a01:Float = b
-		Local a02:Float = c
-		Local a03:Float = d
-		Local a10:Float = e
-		Local a11:Float = f
-		Local a12:Float = g
-		Local a13:Float = h
-		Local a20:Float = i
-		Local a21:Float = j
-		Local a22:Float = k
-		Local a23:Float = l
-		Local a30:Float = m
-		Local a31:Float = n
-		Local a32:Float = o
-		Local a33:Float = p
-		Return New SMat4F(a11 * (a22 * a33 - a23 * a32) - a21 * (a12 * a33 - a13 * a32) + a31 * (a12 * a23 - a13 * a22), ..
-			-(a01 * (a22 * a33 - a23 * a32) - a21 * (a02 * a33 - a03 * a32) + a31 * (a02 * a23 - a03 * a22)), ..
-			a01 * (a12 * a33 - a13 * a32) - a11 * (a02 * a33 - a03 * a32) + a31 * (a02 * a13 - a03 * a12), ..
-			-(a01 * (a12 * a23 - a13 * a22) - a11 * (a02 * a23 - a03 * a22) + a21 * (a02 * a13 - a03 * a12)), ..
-			-(a10 * (a22 * a33 - a23 * a32) - a20 * (a12 * a33 - a13 * a32) + a30 * (a12 * a23 - a13 * a22)), ..
-			a00 * (a22 * a33 - a23 * a32) - a20 * (a02 * a33 - a03 * a32) + a30 * (a02 * a23 - a03 * a22), ..
-			-(a00 * (a12 * a33 - a13 * a32) - a10 * (a02 * a33 - a03 * a32) + a30 * (a02 * a13 - a03 * a12)), ..
-			a00 * (a12 * a23 - a13 * a22) - a10 * (a02 * a23 - a03 * a22) + a20 * (a02 * a13 - a03 * a12), ..
-			a10 * (a21 * a33 - a23 * a31) - a20 * (a11 * a33 - a13 * a31) + a30 * (a11 * a23 - a13 * a21), ..
-			-(a00 * (a21 * a33 - a23 * a31) - a20 * (a01 * a33 - a03 * a31) + a30 * (a01 * a23 - a03 * a21)), ..
-			a00 * (a11 * a33 - a13 * a31) - a10 * (a01 * a33 - a03 * a31) + a30 * (a01 * a13 - a03 * a11), ..
-			-(a00 * (a11 * a23 - a13 * a21) - a10 * (a01 * a23 - a03 * a21) + a20 * (a01 * a13 - a03 * a11)), ..
-			-(a10 * (a21 * a32 - a22 * a31) - a20 * (a11 * a32 - a12 * a31) + a30 * (a11 * a22 - a12 * a21)), ..
-			a00 * (a21 * a32 - a22 * a31) - a20 * (a01 * a32 - a02 * a31) + a30 * (a01 * a22 - a02 * a21), ..
-			-(a00 * (a11 * a32 - a12 * a31) - a10 * (a01 * a32 - a02 * a31) + a30 * (a01 * a12 - a02 * a11)), ..
-			a00 * (a11 * a22 - a12 * a21) - a10 * (a01 * a22 - a02 * a21) + a20 * (a01 * a12 - a02 * a11))
-	End Method
-	
-	Rem
-	bbdoc: Multiplies the matrix by @z by its components, returning a new matrix.
-	End Rem
-	Method CompMul:SMat4F(z:SMat4F Var)
-		Return New SMat4F(a * z.a, b * z.b, c * z.c, d * z.d, ..
-			e * z.e, f * z.f, g * z.g, h * z.h, ..
-			i * z.i, j * z.j, k * z.k, l * z.l, ..
-			m * z.m, n * z.n, o * z.o, p * z.p)
-	End Method
-	
-	Rem
-	bbdoc: Returns the determinant of the matrix.
-	End Rem
-	Method Determinant:Float()
-		Local a00:Float = a
-		Local a01:Float = b
-		Local a02:Float = c
-		Local a03:Float = d
-		Local a10:Float = e
-		Local a11:Float = f
-		Local a12:Float = g
-		Local a13:Float = h
-		Local a20:Float = i
-		Local a21:Float = j
-		Local a22:Float = k
-		Local a23:Float = l
-		Local a30:Float = m
-		Local a31:Float = n
-		Local a32:Float = o
-		Local a33:Float = p
-		Local b00:Float = a00 * a11 - a01 * a10
-		Local b01:Float = a00 * a12 - a02 * a10
-		Local b02:Float = a00 * a13 - a03 * a10
-		Local b03:Float = a01 * a12 - a02 * a11
-		Local b04:Float = a01 * a13 - a03 * a11
-		Local b05:Float = a02 * a13 - a03 * a12
-		Local b06:Float = a20 * a31 - a21 * a30
-		Local b07:Float = a20 * a32 - a22 * a30
-		Local b08:Float = a20 * a33 - a23 * a30
-		Local b09:Float = a21 * a32 - a22 * a31
-		Local b10:Float = a21 * a33 - a23 * a31
-		Local b11:Float = a22 * a33 - a23 * a32
-		Return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06
-	End Method
-
-	Rem
-	bbdoc: Returns a projection matrix with a viewing frustum defined by the plane coordinates passed in.
-	End Rem
-	Function Frustum:SMat4F(l:Float, r:Float, b:Float, t:Float, n:Float, f:Float)
-		Local rl:Float = 1.0 / (r - l)
-		Local tb:Float = 1.0 / (t - b)
-		Local nf:Float = 1.0 / (n - f)
-		Return New SMat4F((2.0 * n) * rl, 0, 0, 0, ..
-			0, (2.0 * n) * tb, 0, 0, ..
-			(r + l) * rl, (t + b) * tb, (f + n) * nf, -1, ..
-			0, 0, (2.0 * n * f) * nf, 0)
-	End Function
-	
-	Rem
-	bbdoc: The inverse of this matrix.
-	about: An inverted matrix is such that if multiplied by the original would result in identity matrix.
-	If some matrix transforms vectors in a particular way, then the inverse matrix can transform them back.
-	End Rem
-	Method Invert:SMat4F()
-		Local a00:Float = a
-		Local a01:Float = b
-		Local a02:Float = c
-		Local a03:Float = d
-		Local a10:Float = e
-		Local a11:Float = f
-		Local a12:Float = g
-		Local a13:Float = h
-		Local a20:Float = i
-		Local a21:Float = j
-		Local a22:Float = k
-		Local a23:Float = l
-		Local a30:Float = m
-		Local a31:Float = n
-		Local a32:Float = o
-		Local a33:Float = p
-		Local b00:Float = a00 * a11 - a01 * a10
-		Local b01:Float = a00 * a12 - a02 * a10
-		Local b02:Float = a00 * a13 - a03 * a10
-		Local b03:Float = a01 * a12 - a02 * a11
-		Local b04:Float = a01 * a13 - a03 * a11
-		Local b05:Float = a02 * a13 - a03 * a12
-		Local b06:Float = a20 * a31 - a21 * a30
-		Local b07:Float = a20 * a32 - a22 * a30
-		Local b08:Float = a20 * a33 - a23 * a30
-		Local b09:Float = a21 * a32 - a22 * a31
-		Local b10:Float = a21 * a33 - a23 * a31
-		Local b11:Float = a22 * a33 - a23 * a32
-		Local det:Float = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06
-		If det = 0 Then
-			Return New SMat4F(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
-		End If
-		det = 1 / det
-		Return New SMat4F((a11 * b11 - a12 * b10 + a13 * b09) * det, ..
-			(a02 * b10 - a01 * b11 - a03 * b09) * det, ..
-			(a31 * b05 - a32 * b04 + a33 * b03) * det, ..
-			(a22 * b04 - a21 * b05 - a23 * b03) * det, ..
-			(a12 * b08 - a10 * b11 - a13 * b07) * det, ..
-			(a00 * b11 - a02 * b08 + a03 * b07) * det, ..
-			(a32 * b02 - a30 * b05 - a33 * b01) * det, ..
-			(a20 * b05 - a22 * b02 + a23 * b01) * det, ..
-			(a10 * b10 - a11 * b08 + a13 * b06) * det, ..
-			(a01 * b08 - a00 * b10 - a03 * b06) * det, ..
-			(a30 * b04 - a31 * b02 + a33 * b00) * det, ..
-			(a21 * b02 - a20 * b04 - a23 * b00) * det, ..
-			(a11 * b07 - a10 * b09 - a12 * b06) * det, ..
-			(a00 * b09 - a01 * b07 + a02 * b06) * det, ..
-			(a31 * b01 - a30 * b03 - a32 * b00) * det, ..
-			(a20 * b03 - a21 * b01 + a22 * b00) * det)
-	End Method
-	
-	Rem
-	bbdoc: Computes a transformation matrix that corresponds to a camera viewing the @eye from the @pos.
-	about: The right-hand vector is perpendicular to the up vector.
-	End Rem
-	Function LookAt:SMat4F(eye:SVec3F, pos:SVec3F, upDir:SVec3F)
-		Local forward:SVec3F = (eye - pos).Normal()
-		Local lft:SVec3F = upDir.Cross(forward).Normal()
-		
-		Local up:SVec3F = forward.Cross(lft)
-		
-		Local mat:SMat4F = SMat4F.Identity()
-		
-		Local a00:Float = lft.x
-		Local a01:Float = up.x
-		Local a02:Float = forward.x
-		Local a03:Float = mat.d
-		Local a10:Float = lft.y
-		Local a11:Float = up.y
-		Local a12:Float = forward.y
-		Local a13:Float = mat.h
-		Local a20:Float = lft.z
-		Local a21:Float = up.z
-		Local a22:Float = forward.z
-		Local a23:Float = mat.l
-		Local a30:Float = -lft.x * eye.x - lft.y * eye.y - lft.z * eye.z
-		Local a31:Float = -up.x * eye.x - up.y * eye.y - up.z * eye.z
-		Local a32:Float = -forward.x * eye.x - forward.y * eye.y - forward.z * eye.z
-		Local a33:Float = mat.p
-
-		Return New SMat4F(a00, a01, a02, a03, a10, a11, a12, a13, a20, a21, a22, a23, a30, a31, a32, a33)
-	End Function
-	
-	Rem
-	bbdoc: Creates an orthogonal projection matrix.
-	about: The returned matrix, when used as a Camera's projection matrix, creates a view showing the area between @width and @height, with @zNear and @zFar as the near and far depth clipping planes.
-	End Rem
-	Function Orthogonal:SMat4F(width:Float, height:Float, zNear:Float, zFar:Float)
-		Local nf:Float = 1.0 / (zNear - zFar)
-		Return New SMat4F(2.0 / width, 0, 0, 0, ..
-			0, 2.0 / height, 0, 0, ..
-			0, 0, 2.0 * nf, 0, ..
-			0, 0, (zNear + zFar) * nf, 1)
-	End Function
-	
-	Rem
-	bbdoc: Creates a perspective projection matrix.
-	End Rem
-	Function Perspective:SMat4F(fov:Float, w:Float, h:Float, n:Float, f:Float)
-		Local tf:Float = Tan(fov / 2)
-		Return New SMat4F(1 / ((w / h) * tf), 0, 0, 0, ..
-			0, 1 / tf, 0, 0, ..
-			0, 0, - (f + n) / (f - n), -1, ..
-			0, 0, - (2 * f * n) / (f - n), 0)
-	End Function
-	
-	Rem
-	bbdoc: Creates a rotation matrix, rotated @angle degrees around the point @axis.
-	End Rem
-	Method Rotate:SMat4F(axis:SVec3F, angle:Double)
-		Local c:Float = Cos(angle)
-		Local ic:Float = 1 - c
-		Local s:Float = Sin(angle)
-
-		Local norm:SVec3F = axis.Normal()
-
-		Local x:Float = ic * norm.x
-		Local y:Float = ic * norm.y
-		Local z:Float = ic * norm.z
-		Local mat:SMat4F = New SMat4F(c + x * norm.x, x * norm.y + s * norm.z, x * norm.z - s * norm.y, 0, ..
-				y * norm.x - s * norm.z, c + y * norm.y, y * norm.z + s * norm.x, 0, ..
-				z * norm.x + s * norm.y, z * norm.y - s * norm.x, c + z * norm.z, 0, ..
-				0, 0, 0, 1)
-		
-		Return Self * mat
-	End Method
-	
-	Rem
-	bbdoc: Returns a rotation matrix on the given @axis and @angle degrees.
-	End Rem
-	Function Rotation:SMat4F(axis:SVec3F, angle:Double)
-		Local x:Float = axis.x
-		Local y:Float = axis.y
-		Local z:Float = axis.z
-		Local sa:Double = Sin(angle)
-		Local ca:Double = Cos(angle)
-		Local t:Float = 1 - ca
-		Return New SMat4F(Float(x * x * t + ca), ..
-			Float(y * x * t + z * sa), ..
-			Float(z * x * t - y * sa), ..
-			0, ..
-			Float(x * y * t - z * sa), ..
-			Float(y * y * t + ca), ..
-			Float(z * y * t + x * sa), ..
-			0, ..
-			Float(x * z * t + y * sa), ..
-			Float(y * z * t - x * sa), ..
-			Float(z * z * t + ca), ..
-			0, 0, 0, 0, 1)
-	End Function
-	
-	Rem
-	bbdoc: Scales the matrix, return the new scaled matrix.
-	End Rem
-	Method Scale:SMat4F(s:SVec3F)
-		Local bx:Float = s.x
-		Local by:Float = s.y
-		Local bz:Float = s.z
-		Return New SMat4F(a * bx, b * bx, c * bx, d * bx, ..
-			e * by, f * by, g * by, h * by, ..
-			i * bz, j * bz, k * bz, l * bz, ..
-			m, n, o, p)
-	End Method
-
-	Rem
-	bbdoc: Scales the matrix, return the new scaled matrix.
-	End Rem
-	Method Scale:SMat4F(s:SVec3D)
-		Local bx:Double = s.x
-		Local by:Double = s.y
-		Local bz:Double = s.z
-		Return New SMat4F(Float(a * bx), Float(b * bx), Float(c * bx), Float(d * bx), ..
-			Float(e * by), Float(f * by), Float(g * by), Float(h * by), ..
-			Float(i * bz), Float(j * bz), Float(k * bz), Float(l * bz), ..
-			m, n, o, p)
-	End Method
-	
-	Rem
-	bbdoc: Creates a scaling matrix.
-	End Rem
-	Function Scaling:SMat4F(s:SVec3F)
-		Return New SMat4F(s.x, 0, 0, 0, 0, s.y, 0, 0, 0, 0, s.z, 0, 0, 0, 0, 1)
-	End Function
-
-	Rem
-	bbdoc: Creates a Scaling matrix.
-	End Rem
-	Function Scaling:SMat4F(s:SVec3D)
-		Return New SMat4F(Float(s.x), 0, 0, 0, 0, Float(s.y), 0, 0, 0, 0, Float(s.z), 0, 0, 0, 0, 1)
-	End Function
-
-	Rem
-	bbdoc: Returns the transpose of this matrix.
-	about: The transposed matrix is the one that has the columns exchanged with its rows.
-	End Rem
-	Method Transpose:SMat4F()
-		Return New SMat4F(a, e, i, m, b, f, j, n, c, g, k, o, d, h, l, p)
-	End Method
-	
-	Rem
-	bbdoc: Translates the matrix to @s.
-	End Rem
-	Method Translate:SMat4F(s:SVec3F)
-		Local bx:Float = s.x
-		Local by:Float = s.y
-		Local bz:Float = s.z
-		Return New SMat4F(a, b, c, d, e, f, g, h, i, j, k, l, ..
-			a * bx + e * by + i * bz + m, ..
-			b * bx + f * by + j * bz + n, ..
-			c * bx + g * by + k * bz + o, ..
-			d * bx + h * by + l * bz + p)
-	End Method
-
-	Rem
-	bbdoc: Translates the matrix To @s.
-	End Rem
-	Method Translate:SMat4F(s:SVec3D)
-		Local bx:Float = s.x
-		Local by:Float = s.y
-		Local bz:Float = s.z
-		Return New SMat4F(a, b, c, d, e, f, g, h, i, j, k, l, ..
-			a * bx + e * by + i * bz + m, ..
-			b * bx + f * by + j * bz + n, ..
-			c * bx + g * by + k * bz + o, ..
-			d * bx + h * by + l * bz + p)
-	End Method
-
-	Rem
-	bbdoc: Creates a translation matrix.
-	End Rem
-	Function Translation:SMat4F(s:SVec3F)
-		Return New SMat4F(1, 0, 0, 0, ..
-			0, 1, 0, 0, ..
-			0, 0, 1, 0, ..
-			s.x, s.y, s.z, 1)
-	End Function
-
-	Rem
-	bbdoc: Creates a translation matrix.
-	End Rem
-	Function Translation:SMat4F(s:SVec3D)
-		Return New SMat4F(1, 0, 0, 0, ..
-			0, 1, 0, 0, ..
-			0, 0, 1, 0, ..
-			Float(s.x), Float(s.y), Float(s.z), 1)
-	End Function
-	
-	Rem
-	bbdoc: Returns a #String representation of the matrix.
-	End Rem
-	Method ToString:String() Override
-		Local sb:TStringBuilder = New TStringBuilder
-		
-		sb.Append(a).Append(", ").Append(e).Append(", ").Append(i).Append(", ").Append(m).Append(",~n")
-		sb.Append(b).Append(", ").Append(f).Append(", ").Append(j).Append(", ").Append(n).Append(",~n")
-		sb.Append(c).Append(", ").Append(g).Append(", ").Append(k).Append(", ").Append(o).Append(",~n")
-		sb.Append(d).Append(", ").Append(h).Append(", ").Append(l).Append(", ").Append(p)
-		
-		Return sb.ToString()
-	End Method
-	
-End Struct
-
-Rem
-bbdoc: An #Int backed 2x2 Matrix.
-End Rem
-Struct SMat2I
-	Field ReadOnly a:Int
-	Field ReadOnly b:Int
-	Field ReadOnly c:Int
-	Field ReadOnly d:Int
-	
-	Rem
-	bbdoc: Creates a new #SMat2I from the supplied arguments.
-	End Rem
-	Method New(a:Int, b:Int, c:Int, d:Int)
-		Self.a = a
-		Self.b = b
-		Self.c = c
-		Self.d = d
-	End Method
-	
-	Rem
-	bbdoc: Applies the matrix to the vector @v, returning a new vector.
-	End Rem
-	Method Apply:SVec2I(v:SVec2I)
-		Return New SVec2I(a * v.x + c * v.y, b * v.x + d * v.y)
-	End Method
-
-	Rem
-	bbdoc: Returns the identity matrix.
-	End Rem
-	Function Identity:SMat2I()
-		Return New SMat2I(1, 0, 0, 1)
-	End Function
-	
-	Rem
-	bbdoc: Adds @z to the matrix, returning a new matrix.
-	End Rem
-	Method Operator+:SMat2I(z:SMat2I)
-		Return New SMat2I(a + z.a, b + z.b, c + z.c, d + z.d)
-	End Method
-	
-	Rem
-	bbdoc: Subtracts @z from the matrix, returning a new matrix.
-	End Rem
-	Method Operator-:SMat2I(z:SMat2I)
-		Return New SMat2I(a - z.a, b - z.b, c - z.c, d - z.d)
-	End Method
-	
-	Rem
-	bbdoc: Multiplies the matrix by @z, the dot product, returning a new matrix.
-	End Rem
-	Method Operator*:SMat2I(z:SMat2I)
-		Return New SMat2I(a * z.a + c * z.b, b * z.a + d * z.b, a * z.c + c * z.d, b * z.c + d * z.d)
-	End Method
-	
-	Rem
-	bbdoc: Returns the transposition of the cofactor matrix.
-	End Rem
-	Method Adjoint:SMat2I()
-		Return New SMat2I(d, -b, -c, a)
-	End Method
-	
-	Rem
-	bbdoc: Multiplies the matrix by @z by its components, return a new matrix.
-	End Rem
-	Method CompMul:SMat2I(z:SMat2I)
-		Return New SMat2I(a * z.a, b * z.b, c * z.c, d * z.d)
-	End Method
-	
-	Rem
-	bbdoc: Returns the determinant of the matrix.
-	End Rem
-	Method Determinant:Int()
-		Return a * d - c * b
-	End Method
-	
-	Rem
-	bbdoc: Returns the inverse of the matrix.
-	End Rem
-	Method Invert:SMat2I()
-		Local det:Double = a * d - c * b
-		If det = 0 Then
-			Return New SMat2I(0, 0, 0, 0)
-		End If
-		det = 1 / det
-		Return New SMat2I(Int(d * det), Int(-b * det), Int(-c * det), Int(a * det))
-	End Method
-	
-	Rem
-	bbdoc: Rotates the matrix by @angle degrees, returning the rotated matrix.
-	End Rem
-	Method Rotate:SMat2I(angle:Double)
-		Local sa:Double = Sin(angle)
-		Local ca:Double = Cos(angle)
-		Return New SMat2I(Int(a * ca + c * sa), Int(b * ca + d * sa), Int(a * -sa + c * ca), Int(b * -sa + d * ca))
-	End Method
-	
-	Rem
-	bbdoc: Creates a rotated matrix of @angle degrees.
-	End Rem
-	Function Rotation:SMat2I(angle:Double)
-		Local sa:Double = Sin(angle)
-		Local ca:Double = Cos(angle)
-		Return New SMat2I(Int(ca), Int(sa), Int(-sa), Int(ca))
-	End Function
-	
-	Rem
-	bbdoc: Returns the scale of this matrix.
-	End Rem
-	Method Scale:SMat2I(s:SVec2I)
-		Return New SMat2I(a * s.x, b * s.x, c * s.y, d * s.y)
-	End Method
-
-	Rem
-	bbdoc: Returns the scale of this matrix.
-	End Rem
-	Method Scale:SMat2I(s:SVec2D)
-		Return New SMat2I(Int(a * s.x), Int(b * s.x), Int(c * s.y), Int(d * s.y))
-	End Method
-
-	Rem
-	bbdoc: Returns the scale of this matrix.
-	End Rem
-	Method Scale:SMat2I(s:SVec2F)
-		Return New SMat2I(Int(a * s.x), Int(b * s.x), Int(c * s.y), Int(d * s.y))
-	End Method
-	
-	Rem
-	bbdoc: Creates a scaled matrix of the scale @s.
-	End Rem
-	Function Scaling:SMat2I(s:SVec2I)
-		Return New SMat2I(s.x, 0, 0, s.y)
-	End Function
-	
-	Rem
-	bbdoc: Returns the transpose of this matrix.
-	End Rem
-	Method Transpose:SMat2I()
-		Return New SMat2I(a, c, b, d)
-	End Method
-	
-	Rem
-	bbdoc: Returns a #String representation of the matrix.
-	End Rem
-	Method ToString:String() Override
-		Local sb:TStringBuilder = New TStringBuilder
-		
-		sb.Append(a).Append(", ").Append(c).Append(",~n")
-		sb.Append(b).Append(", ").Append(d)
-		
-		Return sb.ToString()
-	End Method
-
-End Struct
-
-Rem
-bbdoc: An #Int backed 3x3 matrix.
-End Rem
-Struct SMat3I
-	Field ReadOnly a:Int
-	Field ReadOnly b:Int
-	Field ReadOnly c:Int
-	Field ReadOnly d:Int
-	Field ReadOnly e:Int
-	Field ReadOnly f:Int
-	Field ReadOnly g:Int
-	Field ReadOnly h:Int
-	Field ReadOnly i:Int
-
-	Rem
-	bbdoc: Creates a new #SMat3I from the supplied arguments.
-	End Rem
-	Method New(a:Int, b:Int, c:Int, d:Int, e:Int, f:Int, g:Int, h:Int, i:Int)
-		Self.a = a
-		Self.b = b
-		Self.c = c
-		Self.d = d
-		Self.e = e
-		Self.f = f
-		Self.g = g
-		Self.h = h
-		Self.i = i
-	End Method
-
-	Rem
-	bbdoc: Applies the matrix to the vector @v, returning a new vector.
-	End Rem
-	Method Apply:SVec2I(v:SVec2I)
-		Return New SVec2I(a * v.x + d * v.y + g, b * v.x + e * v.y + h)
-	End Method
-
-	Rem
-	bbdoc: Applies the matrix to the vector @v, returning a new vector.
-	End Rem
-	Method Apply:SVec3I(v:SVec3I)
-		Return New SVec3I(v.x * a + v.y * d + v.z * g, v.x * b + v.y * e + v.z * h, v.x * c + v.y * f + v.z * i)
-	End Method
-
-	Rem
-	bbdoc: Applies the matrix to the vector @v, returning a new vector.
-	End Rem
-	Method Apply:SVec4I(v:SVec4I)
-		Return New SVec4I(v.x * a + v.y * d + v.z * g, v.x * b + v.y * e + v.z * h, v.x * c + v.y * f + v.z * i, 0)
-	End Method
-
-	Rem
-	bbdoc: Return the 3x3 identity matrix.
-	End Rem
-	Function Identity:SMat3I()
-		Return New SMat3I(1, 0, 0, 0, 1, 0, 0, 0, 1)
-	End Function
-	
-	Rem
-	bbdoc: Adds @z to the matrix, returning a new matrix.
-	End Rem
-	Method Operator+:SMat3I(z:SMat3I Var)
-		Return New SMat3I(a + z.a, b + z.b, c + z.c, d + z.d, e + z.e, f + z.f, g + z.g, h + z.h, i + z.i)
-	End Method
-	
-	Rem
-	bbdoc: Subtracts @z from the matrix, returning a new matrix.
-	End Rem
-	Method Operator-:SMat3I(z:SMat3I Var)
-		Return New SMat3I(a - z.a, b - z.b, c - z.c, d - z.d, e - z.e, f - z.f, g - z.g, h - z.h, i - z.i)
-	End Method
-	
-	Rem
-	bbdoc: Multiplies the matrix by @z, the dot product, returning a new matrix.
-	End Rem
-	Method Operator*:SMat3I(z:SMat3I Var)
-		Local a00:Int = a
-		Local a01:Int = b
-		Local a02:Int = c
-		Local a10:Int = d
-		Local a11:Int = e
-		Local a12:Int = f
-		Local a20:Int = g
-		Local a21:Int = h
-		Local a22:Int = i
-		Local b00:Int = z.a
-		Local b01:Int = z.b
-		Local b02:Int = z.c
-		Local b10:Int = z.d
-		Local b11:Int = z.e
-		Local b12:Int = z.f
-		Local b20:Int = z.g
-		Local b21:Int = z.h
-		Local b22:Int = z.i
-		Return New SMat3I(b00 * a00 + b01 * a10 + b02 * a20, ..
-			b00 * a01 + b01 * a11 + b02 * a21, ..
-			b00 * a02 + b01 * a12 + b02 * a22, ..
-			b10 * a00 + b11 * a10 + b12 * a20, ..
-			b10 * a01 + b11 * a11 + b12 * a21, ..
-			b10 * a02 + b11 * a12 + b12 * a22, ..
-			b20 * a00 + b21 * a10 + b22 * a20, ..
-			b20 * a01 + b21 * a11 + b22 * a21, ..
-			b20 * a02 + b21 * a12 + b22 * a22)
-	End Method
-	
-	Rem
-	bbdoc: Returns the transposition of the cofactor matrix.
-	End Rem
-	Method Adjoint:SMat3I()
-		Return New SMat3I(e * i - f * h, ..
-			c * h - b * i, ..
-			b * f - c * e, ..
-			f * g - d * i, ..
-			a * i - c * g, ..
-			c * d - a * f, ..
-			d * h - e * g, ..
-			b * g - a * h, ..
-			a * e - b * d)
-	End Method
-	
-	Rem
-	bbdoc: Multiplies the matrix by @z by its components, return a new matrix.
-	End Rem
-	Method CompMul:SMat3I(z:SMat3I Var)
-		Return New SMat3I(a * z.a, b * z.b, c * z.c, d * z.d, e * z.e, f * z.f, g * z.g, h * z.h, i * z.i)
-	End Method
-	
-	Rem
-	bbdoc: Returns the determinant of the matrix.
-	End Rem
-	Method Determinant:Int()
-		Local a00:Int = a
-		Local a01:Int = b
-		Local a02:Int = c
-		Local a10:Int = d
-		Local a11:Int = e
-		Local a12:Int = f
-		Local a20:Int = g
-		Local a21:Int = h
-		Local a22:Int = i
-		Return a00 * ( a22 * a11 - a12 * a21) + a01 * (-a22 * a10 + a12 * a20) + a02 * ( a21 * a10 - a11 * a20)
-	End Method
-	
-	Rem
-	bbdoc: Returns the inverse of the matrix.
-	End Rem
-	Method Invert:SMat3I()
-		Local a00:Int = a
-		Local a01:Int = b
-		Local a02:Int = c
-		Local a10:Int = d
-		Local a11:Int = e
-		Local a12:Int = f
-		Local a20:Int = g
-		Local a21:Int = h
-		Local a22:Int = i
-		Local b01:Int =  a22 * a11 - a12 * a21
-		Local b11:Int = -a22 * a10 + a12 * a20
-		Local b21:Int =  a21 * a10 - a11 * a20
-		Local det:Double = a00 * b01 + a01 * b11 + a02 * b21
-		If det = 0 Then
-			Return New SMat3I(0, 0, 0, 0, 0, 0, 0, 0, 0)
-		End If
-		det = 1 / det
-		Return New SMat3I(Int(b01 * det), ..
-			Int((-a22 * a01 + a02 * a21) * det), ..
-			Int(( a12 * a01 - a02 * a11) * det),
-			Int(b11 * det), ..
-			Int(( a22 * a00 - a02 * a20) * det), ..
-			Int((-a12 * a00 + a02 * a10) * det), ..
-			Int(b21 * det), ..
-			Int((-a21 * a00 + a01 * a20) * det), ..
-			Int(( a11 * a00 - a01 * a10) * det))
-	End Method
-	
-	Rem
-	bbdoc: Rotates the matrix by @angle degrees, returning a new matrix.
-	End Rem
-	Method Rotate:SMat3I(angle:Double)
-		Local sa:Double = Sin(angle)
-		Local ca:Double = Cos(angle)
-		Return New SMat3I(Int(ca * a + sa * d), ..
-			Int(ca * b + sa * e), ..
-			Int(ca * c + sa * f), ..
-			Int(ca * d - sa * a), ..
-			Int(ca * e - sa * b), ..
-			Int(ca * f - sa * c), ..
-			g, h, i)
-	End Method
-	
-	Rem
-	bbdoc: Retrns a rotation matrix of @angle degrees.
-	End Rem
-	Function Rotation:SMat3I(angle:Double)
-		Local sa:Double = Sin(angle)
-		Local ca:Double = Cos(angle)
-		Return New SMat3I(Int(ca), Int(sa), 0, Int(-sa), Int(ca), 0, 0, 0, 1)
-	End Function
-	
-	Rem
-	bbdoc: Scales the matrix by @s, returning a new matrix.
-	End Rem
-	Method Scale:SMat3I(s:SVec2I)
-		Local bx:Int = s.x
-		Local by:Int = s.y
-		Return New SMat3I(a * bx, b * bx, c * bx, d * by, e * by, f * by, g, h, i)
-	End Method
-
-	Rem
-	bbdoc: Scales the matrix by @s, returning a new matrix.
-	End Rem
-	Method Scale:SMat3I(s:SVec2D)
-		Local bx:Int = s.x
-		Local by:Int = s.y
-		Return New SMat3I(Int(a * bx), Int(b * bx), Int(c * bx), Int(d * by), Int(e * by), Int(f * by), g, h, i)
-	End Method
-
-	Rem
-	bbdoc: Scales the matrix by @s, returning a new matrix.
-	End Rem
-	Method Scale:SMat3I(s:SVec2F)
-		Local bx:Int = s.x
-		Local by:Int = s.y
-		Return New SMat3I(Int(a * bx), Int(b * bx), Int(c * bx), Int(d * by), Int(e * by), Int(f * by), g, h, i)
-	End Method
-	
-	Rem
-	bbdoc: Returns a scaling matrix of @s.
-	End Rem
-	Function Scaling:SMat3I(s:SVec2I)
-		Return New SMat3I(s.x, 0, 0, 0, s.y, 0, 0, 0, 1)
-	End Function
-
-	Rem
-	bbdoc: Returns a scaling matrix of @s.
-	End Rem
-	Function Scaling:SMat3I(s:SVec2D)
-		Return New SMat3I(Int(s.x), 0, 0, 0, Int(s.y), 0, 0, 0, 1)
-	End Function
-
-	Rem
-	bbdoc: Returns a scaling matrix of @s.
-	End Rem
-	Function Scaling:SMat3I(s:SVec2F)
-		Return New SMat3I(Int(s.x), 0, 0, 0, Int(s.y), 0, 0, 0, 1)
-	End Function
-
-	Rem
-	bbdoc: Returns a translation with the specified @x, @y, and @z displacements.
-	End Rem
-	Method Translate:SMat3I(x:Int, y:Int, z:Int)
-		Return New SMat3I( ..
-			a, b, c, ..
-			d, e, f, ..
-			g + a * x + b * y + c * z, ..
-			h + d * x + e * y + f * z, ..
-			i + g * x + h * y + i * z)
-	End Method
-
-	Rem
-	bbdoc: Returns a translation with displacement vector @s.
-	End Rem
-	Method Translate:SMat3I(t:SVec3I)
-		Return New SMat3I( ..
-			a, b, c, ..
-			d, e, f, ..
-			g + a * t.x + b * t.y + c * t.z, ..
-			h + d * t.x + e * t.y + f * t.z, ..
-			i + g * t.x + h * t.y + i * t.z)
-	End Method
-
-	Rem
-	bbdoc: Returns a transposition of the matrix.
-	End Rem
-	Method Transpose:SMat3I()
-		Return New SMat3I(a, d, g, b, e, h, c, f, i)
-	End Method
-	
-	Rem
-	bbdoc: Returns a #String representation of the matrix.
-	End Rem
-	Method ToString:String() Override
-		Local sb:TStringBuilder = New TStringBuilder
-		
-		sb.Append(a).Append(", ").Append(d).Append(", ").Append(g).Append(",~n")
-		sb.Append(b).Append(", ").Append(e).Append(", ").Append(h).Append(",~n")
-		sb.Append(c).Append(", ").Append(f).Append(", ").Append(i)
-		
-		Return sb.ToString()
-	End Method
-	
-End Struct
-
-Rem
-bbdoc: A standard #Int backed 4x4 transformation matrix.
-End Rem
-Struct SMat4I
-	Field ReadOnly a:Int
-	Field ReadOnly b:Int
-	Field ReadOnly c:Int
-	Field ReadOnly d:Int
-	Field ReadOnly e:Int
-	Field ReadOnly f:Int
-	Field ReadOnly g:Int
-	Field ReadOnly h:Int
-	Field ReadOnly i:Int
-	Field ReadOnly j:Int
-	Field ReadOnly k:Int
-	Field ReadOnly l:Int
-	Field ReadOnly m:Int
-	Field ReadOnly n:Int
-	Field ReadOnly o:Int
-	Field ReadOnly p:Int
-
-	Rem
-	bbdoc: Creates a new #SMat4I from the supplied arguments.
-	End Rem
-	Method New(a:Int, b:Int, c:Int, d:Int, e:Int, f:Int, g:Int, h:Int, i:Int, j:Int, k:Int, l:Int, m:Int, n:Int, o:Int, p:Int)
-		Self.a = a
-		Self.b = b
-		Self.c = c
-		Self.d = d
-		Self.e = e
-		Self.f = f
-		Self.g = g
-		Self.h = h
-		Self.i = i
-		Self.j = j
-		Self.k = k
-		Self.l = l
-		Self.m = m
-		Self.n = n
-		Self.o = o
-		Self.p = p
-	End Method
-
-	Rem
-	bbdoc: Applies the matrix to the vector @v, returning a new vector.
-	End Rem
-	Method Apply:SVec2I(v:SVec2I)
-		Return New SVec2I(a * v.x + e * v.y + m, b * v.x + f * v.y + n)
-	End Method
-
-	Rem
-	bbdoc: Applies the 4x4 matrix @b to the vector, returning a new vector.
-	End Rem
-	Method Apply:SVec3I(v:SVec3I)
-		Local w:Double = d * v.x + h * v.y + l * v.z + p
-		If w = 0 Then
-			w = 1
-		Else
-			w = 1 / w
-		End If
-		Return New SVec3I(Int((a * v.x + e * v.y + i * v.z + m) * w), ..
-			Int((b * v.x + f * v.y + j * v.z + n) * w), ..
-			Int((c * v.x + g * v.y + k * v.z + o) * w))
-	End Method
-
-	Rem
-	bbdoc: Applies the 4x4 matrix @b to the vector, returning a new vector.
-	End Rem
-	Method Apply:SVec4I(v:SVec4I)
-		Return New SVec4I(a * v.x + e * v.y + i * v.z + m * v.w, ..
-			b * v.x + f * v.y + j * v.z + n * v.w, ..
-			c * v.x + g * v.y + k * v.z + o * v.w, ..
-			d * v.x + h * v.y + l * v.z + p * v.w)
-	End Method
-
-	Rem
-	bbdoc: Returns the identity matrix.
-	End Rem
-	Function Identity:SMat4I()
-		Return New SMat4I(1, 0, 0, 0, ..
-				0, 1, 0, 0, ..
-				0, 0, 1, 0, ..
-				0, 0, 0, 1)
-	End Function
-	
-	Rem
-	bbdoc: Adds @z to the matrix, returning a new matrix.
-	End Rem
-	Method Operator+:SMat4I(z:SMat4I Var)
-		Return New SMat4I(a + z.a, b + z.b, c + z.c, d + z.d, ..
-			e + z.e, f + z.f, g + z.g, h + z.h, ..
-			i + z.i, j + z.j, k + z.k, l + z.l, ..
-			m + z.m, n + z.n, o + z.o, p + z.p)
-	End Method
-	
-	Rem
-	bbdoc: Subtracts @z from the matrix, returning a new matrix.
-	End Rem
-	Method Operator-:SMat4I(z:SMat4I Var)
-		Return New SMat4I(a - z.a, b - z.b, c - z.c, d - z.d, ..
-			e - z.e, f - z.f, g - z.g, h - z.h, ..
-			i - z.i, j - z.j, k - z.k, l - z.l, ..
-			m - z.m, n - z.n, o - z.o, p - z.p)
-	End Method
-
-	Rem
-	bbdoc: Multiplies the matrix by @z, the dot product, returning a new matrix. 
-	End Rem
-	Method Operator*:SMat4I(z:SMat4I Var)
-		Local a00:Int = a
-		Local a01:Int = b
-		Local a02:Int = c
-		Local a03:Int = d
-		Local a10:Int = e
-		Local a11:Int = f
-		Local a12:Int = g
-		Local a13:Int = h
-		Local a20:Int = i
-		Local a21:Int = j
-		Local a22:Int = k
-		Local a23:Int = l
-		Local a30:Int = m
-		Local a31:Int = n
-		Local a32:Int = o
-		Local a33:Int = p
-		Local b00:Int = z.a
-		Local b01:Int = z.b
-		Local b02:Int = z.c
-		Local b03:Int = z.d
-		Local b10:Int = z.e
-		Local b11:Int = z.f
-		Local b12:Int = z.g
-		Local b13:Int = z.h
-		Local b20:Int = z.i
-		Local b21:Int = z.j
-		Local b22:Int = z.k
-		Local b23:Int = z.l
-		Local b30:Int = z.m
-		Local b31:Int = z.n
-		Local b32:Int = z.o
-		Local b33:Int = z.p
-		Return New SMat4I(b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30, ..
-			b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31, ..
-			b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32, ..
-			b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33, ..
-			b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30, ..
-			b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31, ..
-			b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32, ..
-			b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33, ..
-			b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30, ..
-			b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31, ..
-			b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32, ..
-			b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33, ..
-			b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30, ..
-			b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31, ..
-			b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32, ..
-			b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33)
-	End Method
-	
-	Rem
-	bbdoc: Returns the transposition of the cofactor matrix.
-	End Rem
-	Method Adjoint:SMat4I()
-		Local a00:Int = a
-		Local a01:Int = b
-		Local a02:Int = c
-		Local a03:Int = d
-		Local a10:Int = e
-		Local a11:Int = f
-		Local a12:Int = g
-		Local a13:Int = h
-		Local a20:Int = i
-		Local a21:Int = j
-		Local a22:Int = k
-		Local a23:Int = l
-		Local a30:Int = m
-		Local a31:Int = n
-		Local a32:Int = o
-		Local a33:Int = p
-		Return New SMat4I(a11 * (a22 * a33 - a23 * a32) - a21 * (a12 * a33 - a13 * a32) + a31 * (a12 * a23 - a13 * a22), ..
-			-(a01 * (a22 * a33 - a23 * a32) - a21 * (a02 * a33 - a03 * a32) + a31 * (a02 * a23 - a03 * a22)), ..
-			a01 * (a12 * a33 - a13 * a32) - a11 * (a02 * a33 - a03 * a32) + a31 * (a02 * a13 - a03 * a12), ..
-			-(a01 * (a12 * a23 - a13 * a22) - a11 * (a02 * a23 - a03 * a22) + a21 * (a02 * a13 - a03 * a12)), ..
-			-(a10 * (a22 * a33 - a23 * a32) - a20 * (a12 * a33 - a13 * a32) + a30 * (a12 * a23 - a13 * a22)), ..
-			a00 * (a22 * a33 - a23 * a32) - a20 * (a02 * a33 - a03 * a32) + a30 * (a02 * a23 - a03 * a22), ..
-			-(a00 * (a12 * a33 - a13 * a32) - a10 * (a02 * a33 - a03 * a32) + a30 * (a02 * a13 - a03 * a12)), ..
-			a00 * (a12 * a23 - a13 * a22) - a10 * (a02 * a23 - a03 * a22) + a20 * (a02 * a13 - a03 * a12), ..
-			a10 * (a21 * a33 - a23 * a31) - a20 * (a11 * a33 - a13 * a31) + a30 * (a11 * a23 - a13 * a21), ..
-			-(a00 * (a21 * a33 - a23 * a31) - a20 * (a01 * a33 - a03 * a31) + a30 * (a01 * a23 - a03 * a21)), ..
-			a00 * (a11 * a33 - a13 * a31) - a10 * (a01 * a33 - a03 * a31) + a30 * (a01 * a13 - a03 * a11), ..
-			-(a00 * (a11 * a23 - a13 * a21) - a10 * (a01 * a23 - a03 * a21) + a20 * (a01 * a13 - a03 * a11)), ..
-			-(a10 * (a21 * a32 - a22 * a31) - a20 * (a11 * a32 - a12 * a31) + a30 * (a11 * a22 - a12 * a21)), ..
-			a00 * (a21 * a32 - a22 * a31) - a20 * (a01 * a32 - a02 * a31) + a30 * (a01 * a22 - a02 * a21), ..
-			-(a00 * (a11 * a32 - a12 * a31) - a10 * (a01 * a32 - a02 * a31) + a30 * (a01 * a12 - a02 * a11)), ..
-			a00 * (a11 * a22 - a12 * a21) - a10 * (a01 * a22 - a02 * a21) + a20 * (a01 * a12 - a02 * a11))
-	End Method
-	
-	Rem
-	bbdoc: Multiplies the matrix by @z by its components, returning a new matrix.
-	End Rem
-	Method CompMul:SMat4I(z:SMat4I Var)
-		Return New SMat4I(a * z.a, b * z.b, c * z.c, d * z.d, ..
-			e * z.e, f * z.f, g * z.g, h * z.h, ..
-			i * z.i, j * z.j, k * z.k, l * z.l, ..
-			m * z.m, n * z.n, o * z.o, p * z.p)
-	End Method
-	
-	Rem
-	bbdoc: Returns the determinant of the matrix.
-	End Rem
-	Method Determinant:Int()
-		Local a00:Int = a
-		Local a01:Int = b
-		Local a02:Int = c
-		Local a03:Int = d
-		Local a10:Int = e
-		Local a11:Int = f
-		Local a12:Int = g
-		Local a13:Int = h
-		Local a20:Int = i
-		Local a21:Int = j
-		Local a22:Int = k
-		Local a23:Int = l
-		Local a30:Int = m
-		Local a31:Int = n
-		Local a32:Int = o
-		Local a33:Int = p
-		Local b00:Int = a00 * a11 - a01 * a10
-		Local b01:Int = a00 * a12 - a02 * a10
-		Local b02:Int = a00 * a13 - a03 * a10
-		Local b03:Int = a01 * a12 - a02 * a11
-		Local b04:Int = a01 * a13 - a03 * a11
-		Local b05:Int = a02 * a13 - a03 * a12
-		Local b06:Int = a20 * a31 - a21 * a30
-		Local b07:Int = a20 * a32 - a22 * a30
-		Local b08:Int = a20 * a33 - a23 * a30
-		Local b09:Int = a21 * a32 - a22 * a31
-		Local b10:Int = a21 * a33 - a23 * a31
-		Local b11:Int = a22 * a33 - a23 * a32
-		Return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06
-	End Method
-
-	Rem
-	bbdoc: Returns a projection matrix with a viewing frustum defined by the plane coordinates passed in.
-	End Rem
-	Function Frustum:SMat4I(l:Double, r:Double, b:Double, t:Double, n:Double, f:Double)
-		Local rl:Double = 1.0 / (r - l)
-		Local tb:Double = 1.0 / (t - b)
-		Local nf:Double = 1.0 / (n - f)
-		Return New SMat4I(Int((2.0 * n) * rl), 0, 0, 0, ..
-			0, Int((2.0 * n) * tb), 0, 0, ..
-			Int((r + l) * rl), Int((t + b) * tb), Int((f + n) * nf), -1, ..
-			0, 0, Int((2.0 * n * f) * nf), 0)
-	End Function
-	
-	Rem
-	bbdoc: The inverse of this matrix.
-	about: An inverted matrix is such that if multiplied by the original would result in identity matrix.
-	If some matrix transforms vectors in a particular way, then the inverse matrix can transform them back.
-	End Rem
-	Method Invert:SMat4I()
-		Local a00:Int = a
-		Local a01:Int = b
-		Local a02:Int = c
-		Local a03:Int = d
-		Local a10:Int = e
-		Local a11:Int = f
-		Local a12:Int = g
-		Local a13:Int = h
-		Local a20:Int = i
-		Local a21:Int = j
-		Local a22:Int = k
-		Local a23:Int = l
-		Local a30:Int = m
-		Local a31:Int = n
-		Local a32:Int = o
-		Local a33:Int = p
-		Local b00:Int = a00 * a11 - a01 * a10
-		Local b01:Int = a00 * a12 - a02 * a10
-		Local b02:Int = a00 * a13 - a03 * a10
-		Local b03:Int = a01 * a12 - a02 * a11
-		Local b04:Int = a01 * a13 - a03 * a11
-		Local b05:Int = a02 * a13 - a03 * a12
-		Local b06:Int = a20 * a31 - a21 * a30
-		Local b07:Int = a20 * a32 - a22 * a30
-		Local b08:Int = a20 * a33 - a23 * a30
-		Local b09:Int = a21 * a32 - a22 * a31
-		Local b10:Int = a21 * a33 - a23 * a31
-		Local b11:Int = a22 * a33 - a23 * a32
-		Local det:Int = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06
-		If det = 0 Then
-			Return New SMat4I(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
-		End If
-		det = 1 / det
-		Return New SMat4I((a11 * b11 - a12 * b10 + a13 * b09) * det, ..
-			(a02 * b10 - a01 * b11 - a03 * b09) * det, ..
-			(a31 * b05 - a32 * b04 + a33 * b03) * det, ..
-			(a22 * b04 - a21 * b05 - a23 * b03) * det, ..
-			(a12 * b08 - a10 * b11 - a13 * b07) * det, ..
-			(a00 * b11 - a02 * b08 + a03 * b07) * det, ..
-			(a32 * b02 - a30 * b05 - a33 * b01) * det, ..
-			(a20 * b05 - a22 * b02 + a23 * b01) * det, ..
-			(a10 * b10 - a11 * b08 + a13 * b06) * det, ..
-			(a01 * b08 - a00 * b10 - a03 * b06) * det, ..
-			(a30 * b04 - a31 * b02 + a33 * b00) * det, ..
-			(a21 * b02 - a20 * b04 - a23 * b00) * det, ..
-			(a11 * b07 - a10 * b09 - a12 * b06) * det, ..
-			(a00 * b09 - a01 * b07 + a02 * b06) * det, ..
-			(a31 * b01 - a30 * b03 - a32 * b00) * det, ..
-			(a20 * b03 - a21 * b01 + a22 * b00) * det)
-	End Method
-	
-	Rem
-	bbdoc: Computes a transformation matrix that corresponds to a camera viewing the @eye from the @pos.
-	about: The right-hand vector is perpendicular to the up vector.
-	End Rem
-	Function LookAt:SMat4I(eye:SVec3I, pos:SVec3I, up:SVec3I)
-		Local ex:Int = eye.x
-		Local ey:Int = eye.y
-		Local ez:Int = eye.z
-		Local px:Int = pos.x
-		Local py:Int = pos.y
-		Local pz:Int = pos.z
-		Local ux:Int = up.x
-		Local uy:Int = up.y
-		Local uz:Int = up.z
-		Local z0:Int = ex - px
-		Local z1:Int = ey - py
-		Local z2:Int = ez - pz
-		
-		If z0 = 0 Or z1 = 0 Or z2 = 0 Then
-			Return Identity()
-		End If
-		
-		Local length:Int = Sqr(z0 * z0 + z1 * z1 + z2 * z2)
-		z0 :* length
-		z1 :* length
-		z2 :* length
-		
-		Local x0:Int = uy * z2 - uz * z1
-		Local x1:Int = uz * z0 - ux * z2
-		Local x2:Int = ux * z1 - uy * z0
-		
-		length = Sqr(x0 * x0 + x1 * x1 + x2 * x2)
-		
-		If length = 0 Then
-			x0 = 0
-			x1 = 0
-			x2 = 0
-		Else
-			length = 1 / length
-			x0 :* length
-			x1 :* length
-			x2 :* length
-		End If
-		
-		Local y0:Int = z1 * x2 - z2 * x1
-		Local y1:Int = z2 * x0 - z0 * x2
-		Local y2:Int = z0 * x1 - z1 * x0
-		
-		length = Sqr(y0 * y0 + y1 * y1 + y2 * y2)
-		If length = 0 Then
-			y0 = 0
-			y1 = 0
-			y2 = 0
-		Else
-			length = 1 / length
-			y0 :* length
-			y1 :* length
-			y2 :* length
-		End If
-		
-		Return New SMat4I(x0, y0, z0, 0, x1, y1, z1, 0, x2, y2, z2, 0, ..
-			-(x0 * ex + x1 * ey + x2 * ez), -(y0 * ex + y1 * ey + y2 * ez), -(z0 * ex + z1 * ey + z2 * ez), 1)
-	End Function
-	
-	Rem
-	bbdoc: Creates an orthogonal projection matrix.
-	about: The returned matrix, when used as a Camera's projection matrix, creates a view showing the area between @width and @height, with @zNear and @zFar as the near and far depth clipping planes.
-	End Rem
-	Function Orthogonal:SMat4I(width:Double, height:Double, zNear:Double, zFar:Double)
-		Local nf:Double = 1.0 / (zNear - zFar)
-		Return New SMat4I(Int(2.0 / width), 0, 0, 0, ..
-			0, Int(2.0 / height), 0, 0, ..
-			0, 0, Int(2.0 * nf), 0, ..
-			0, 0, Int((zNear + zFar) * nf), 1)
-	End Function
-	
-	Rem
-	bbdoc: Creates a Perspective projection matrix.
-	End Rem
-	Function Perspective:SMat4I(fov:Double, w:Double, h:Double, n:Double, f:Double)
-		Local ft:Double = 1.0 / Tan(fov * 0.5)
-		Local nf:Double = 1.0 / (n - f)
-		Return New SMat4I(Int(ft), 0, 0, 0, ..
-			0, Int(ft * w / h), 0, 0, ..
-			0, 0, Int((f + n) * nf), -1, ..
-			0, 0, Int((2.0 * f * n) * nf), 0) 
-	End Function
-	
-	Rem
-	bbdoc: Creates a rotation matrix, rotated @angle degrees around the point @axis.
-	End Rem
-	Method Rotate:SMat4I(axis:SVec3I, angle:Double)
-		Local x:Int = axis.x
-		Local y:Int = axis.y
-		Local z:Int = axis.z
-		Local a00:Int = a
-		Local a01:Int = b
-		Local a02:Int = c
-		Local a03:Int = d
-		Local a10:Int = e
-		Local a11:Int = f
-		Local a12:Int = g
-		Local a13:Int = h
-		Local a20:Int = i
-		Local a21:Int = j
-		Local a22:Int = k
-		Local a23:Int = l
-		Local sa:Double = Sin(angle)
-		Local ca:Double = Cos(angle)
-		Local t:Double = 1 - ca
-		Local b00:Double = x * x * t + ca
-		Local b01:Double = y * x * t + z * sa
-		Local b02:Double = z * x * t - y * sa
-		Local b10:Double = x * y * t - z * sa
-		Local b11:Double = y * y * t + ca
-		Local b12:Double = z * y * t + x * sa
-		Local b20:Double = x * z * t + y * sa
-		Local b21:Double = y * z * t - x * sa
-		Local b22:Double = z * z * t + ca
-		Return New SMat4I(Int(a00 * b00 + a10 * b01 + a20 * b02), ..
-			Int(a01 * b00 + a11 * b01 + a21 * b02), ..
-			Int(a02 * b00 + a12 * b01 + a22 * b02), ..
-			Int(a03 * b00 + a13 * b01 + a23 * b02), ..
-			Int(a00 * b10 + a10 * b11 + a20 * b12), ..
-			Int(a01 * b10 + a11 * b11 + a21 * b12), ..
-			Int(a02 * b10 + a12 * b11 + a22 * b12), ..
-			Int(a03 * b10 + a13 * b11 + a23 * b12), ..
-			Int(a00 * b20 + a10 * b21 + a20 * b22), ..
-			Int(a01 * b20 + a11 * b21 + a21 * b22), ..
-			Int(a02 * b20 + a12 * b21 + a22 * b22), ..
-			Int(a03 * b20 + a13 * b21 + a23 * b22), ..
-			m, n, o, p)
-	End Method
-	
-	Rem
-	bbdoc: Returns a rotation matrix on the given @axis and @angle degrees.
-	End Rem
-	Function Rotation:SMat4I(axis:SVec3I, angle:Double)
-		Local x:Int = axis.x
-		Local y:Int = axis.y
-		Local z:Int = axis.z
-		Local sa:Double = Sin(angle)
-		Local ca:Double = Cos(angle)
-		Local t:Double = 1 - ca
-		Return New SMat4I(Int(x * x * t + ca), ..
-			Int(y * x * t + z * sa), ..
-			Int(z * x * t - y * sa), ..
-			0, ..
-			Int(x * y * t - z * sa), ..
-			Int(y * y * t + ca), ..
-			Int(z * y * t + x * sa), ..
-			0, ..
-			Int(x * z * t + y * sa), ..
-			Int(y * z * t - x * sa), ..
-			Int(z * z * t + ca), ..
-			0, 0, 0, 0, 1)
-	End Function
-	
-	Rem
-	bbdoc: Scales the matrix, return the new scaled matrix.
-	End Rem
-	Method Scale:SMat4I(s:SVec3I)
-		Local bx:Int = s.x
-		Local by:Int = s.y
-		Local bz:Int = s.z
-		Return New SMat4I(a * bx, b * bx, c * bx, d * bx, ..
-			e * by, f * by, g * by, h * by, ..
-			i * bz, j * bz, k * bz, l * bz, ..
-			m, n, o, p)
-	End Method
-
-	Rem
-	bbdoc: Scales the matrix, return the new scaled matrix.
-	End Rem
-	Method Scale:SMat4I(s:SVec3D)
-		Local bx:Double = s.x
-		Local by:Double = s.y
-		Local bz:Double = s.z
-		Return New SMat4I(Int(a * bx), Int(b * bx), Int(c * bx), Int(d * bx), ..
-			Int(e * by), Int(f * by), Int(g * by), Int(h * by), ..
-			Int(i * bz), Int(j * bz), Int(k * bz), Int(l * bz), ..
-			m, n, o, p)
-	End Method
-
-	Rem
-	bbdoc: Scales the matrix, return the new scaled matrix.
-	End Rem
-	Method Scale:SMat4I(s:SVec3F)
-		Local bx:Float = s.x
-		Local by:Float = s.y
-		Local bz:Float = s.z
-		Return New SMat4I(Int(a * bx), Int(b * bx), Int(c * bx), Int(d * bx), ..
-			Int(e * by), Int(f * by), Int(g * by), Int(h * by), ..
-			Int(i * bz), Int(j * bz), Int(k * bz), Int(l * bz), ..
-			m, n, o, p)
-	End Method
-	
-	Rem
-	bbdoc: Creates a scaling matrix.
-	End Rem
-	Function Scaling:SMat4I(s:SVec3I)
-		Return New SMat4I(s.x, 0, 0, 0, 0, s.y, 0, 0, 0, 0, s.z, 0, 0, 0, 0, 1)
-	End Function
-
-	Rem
-	bbdoc: Creates a scaling matrix.
-	End Rem
-	Function Scaling:SMat4I(s:SVec3D)
-		Return New SMat4I(Int(s.x), 0, 0, 0, 0, Int(s.y), 0, 0, 0, 0, Int(s.z), 0, 0, 0, 0, 1)
-	End Function
-
-	Rem
-	bbdoc: Creates a scaling matrix.
-	End Rem
-	Function Scaling:SMat4I(s:SVec3F)
-		Return New SMat4I(Int(s.x), 0, 0, 0, 0, Int(s.y), 0, 0, 0, 0, Int(s.z), 0, 0, 0, 0, 1)
-	End Function
-
-	Rem
-	bbdoc: Returns the transpose of this matrix.
-	about: The transposed matrix is the one that has the columns exchanged with its rows.
-	End Rem
-	Method Transpose:SMat4I()
-		Return New SMat4I(a, e, i, m, b, f, j, n, c, g, k, o, d, h, l, p)
-	End Method
-	
-	Rem
-	bbdoc: Translates the matrix to @s.
-	End Rem
-	Method Translate:SMat4I(s:SVec3I)
-		Local bx:Int = s.x
-		Local by:Int = s.y
-		Local bz:Int = s.z
-		Return New SMat4I(a, b, c, d, e, f, g, h, i, j, k, l, ..
-			a * bx + e * by + i * bz + m, ..
-			b * bx + f * by + j * bz + n, ..
-			c * bx + g * by + k * bz + o, ..
-			d * bx + h * by + l * bz + p)
-	End Method
-
-	Rem
-	bbdoc: Translates the matrix to @s.
-	End Rem
-	Method Translate:SMat4I(s:SVec3D)
-		Local bx:Double = s.x
-		Local by:Double = s.y
-		Local bz:Double = s.z
-		Return New SMat4I(a, b, c, d, e, f, g, h, i, j, k, l, ..
-			Int(a * bx + e * by + i * bz + m), ..
-			Int(b * bx + f * by + j * bz + n), ..
-			Int(c * bx + g * by + k * bz + o), ..
-			Int(d * bx + h * by + l * bz + p))
-	End Method
-
-	Rem
-	bbdoc: Translates the matrix To @s.
-	End Rem
-	Method Translate:SMat4I(s:SVec3F)
-		Local bx:Float = s.x
-		Local by:Float = s.y
-		Local bz:Float = s.z
-		Return New SMat4I(a, b, c, d, e, f, g, h, i, j, k, l, ..
-			Int(a * bx + e * by + i * bz + m), ..
-			Int(b * bx + f * by + j * bz + n), ..
-			Int(c * bx + g * by + k * bz + o), ..
-			Int(d * bx + h * by + l * bz + p))
-	End Method
-
-	Rem
-	bbdoc: Creates a translation matrix.
-	End Rem
-	Function Translation:SMat4I(s:SVec3I)
-		Return New SMat4I(1, 0, 0, 0, ..
-			0, 1, 0, 0, ..
-			0, 0, 1, 0, ..
-			s.x, s.y, s.z, 1)
-	End Function
-
-	Rem
-	bbdoc: Creates a translation matrix.
-	End Rem
-	Function Translation:SMat4I(s:SVec3D)
-		Return New SMat4I(1, 0, 0, 0, ..
-			0, 1, 0, 0, ..
-			0, 0, 1, 0, ..
-			Int(s.x), Int(s.y), Int(s.z), 1)
-	End Function
-
-	Rem
-	bbdoc: Creates a translation matrix.
-	End Rem
-	Function Translation:SMat4I(s:SVec3F)
-		Return New SMat4I(1, 0, 0, 0, ..
-			0, 1, 0, 0, ..
-			0, 0, 1, 0, ..
-			Int(s.x), Int(s.y), Int(s.z), 1)
-	End Function
-	
-	Rem
-	bbdoc: Returns a #String representation of the matrix.
-	End Rem
-	Method ToString:String() Override
-		Local sb:TStringBuilder = New TStringBuilder
-		
-		sb.Append(a).Append(", ").Append(e).Append(", ").Append(i).Append(", ").Append(m).Append(",~n")
-		sb.Append(b).Append(", ").Append(f).Append(", ").Append(j).Append(", ").Append(n).Append(",~n")
-		sb.Append(c).Append(", ").Append(g).Append(", ").Append(k).Append(", ").Append(o).Append(",~n")
-		sb.Append(d).Append(", ").Append(h).Append(", ").Append(l).Append(", ").Append(p)
-		
-		Return sb.ToString()
-	End Method
-	
-End Struct

+ 0 - 30
polygon.mod/common.bmx

@@ -1,30 +0,0 @@
-' 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

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

@@ -1,27 +0,0 @@
-## 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

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

@@ -1,151 +0,0 @@
-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::
-)

+ 0 - 15
polygon.mod/earcut/LICENSE

@@ -1,15 +0,0 @@
-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.

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

@@ -1,131 +0,0 @@
-## 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).

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

@@ -1,33 +0,0 @@
-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"

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

@@ -1,816 +0,0 @@
-#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);
-}
-}

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

@@ -1,82 +0,0 @@
-#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;
-}

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

@@ -1,43 +0,0 @@
-#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_;
-};

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

@@ -1,105 +0,0 @@
-#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;
-};

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

@@ -1,25 +0,0 @@
-** 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.

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

@@ -1,191 +0,0 @@
-/*
-** 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 );
-}

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

@@ -1,51 +0,0 @@
-/*
-** 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

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

@@ -1,109 +0,0 @@
-/*
-** 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;
-}

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

@@ -1,74 +0,0 @@
-/*
-** 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

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

@@ -1,261 +0,0 @@
-/*
-** 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 );
-	}
-}

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

@@ -1,76 +0,0 @@
-/*
-** 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

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

@@ -1,843 +0,0 @@
-/*
-** 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

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

@@ -1,267 +0,0 @@
-/*
-** 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

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

@@ -1,514 +0,0 @@
-/*
-** 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;
-	}
-}

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

@@ -1,104 +0,0 @@
-/*
-** 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

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

@@ -1,1326 +0,0 @@
-/*
-** 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;
-}

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

@@ -1,74 +0,0 @@
-/*
-** 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

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

@@ -1,982 +0,0 @@
-/*
-** 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;
-}

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

@@ -1,90 +0,0 @@
-/*
-** 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

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

@@ -1,221 +0,0 @@
-/*
-** 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

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

@@ -1,79 +0,0 @@
-'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);
-});

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

@@ -1,13 +0,0 @@
-// 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}},
-});
-
-}
-}

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

@@ -1,16 +0,0 @@
-// 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}},
-});
-
-}
-}

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

@@ -1,17 +0,0 @@
-// 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}},
-});
-
-}
-}

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

@@ -1,13 +0,0 @@
-// 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}},
-});
-
-}
-}

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

@@ -1,13 +0,0 @@
-// 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}},
-});
-
-}
-}

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

@@ -1,13 +0,0 @@
-// 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}},
-});
-
-}
-}

Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 8
polygon.mod/earcut/test/fixtures/dude.cpp


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

@@ -1,15 +0,0 @@
-// 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}},
-});
-
-}
-}

Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 8
polygon.mod/earcut/test/fixtures/eberly_6.cpp


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

@@ -1,14 +0,0 @@
-// 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}},
-});
-
-}
-}

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

@@ -1,17 +0,0 @@
-// 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}},
-});
-
-}
-}

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

@@ -1,106 +0,0 @@
-#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;
-    }
-};
-
-}
-}

Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 8
polygon.mod/earcut/test/fixtures/hilbert.cpp


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

@@ -1,14 +0,0 @@
-// 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}},
-});
-
-}
-}

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

@@ -1,13 +0,0 @@
-// 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}},
-});
-
-}
-}

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

@@ -1,14 +0,0 @@
-// 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}},
-});
-
-}
-}

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

@@ -1,14 +0,0 @@
-// 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}},
-});
-
-}
-}

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

@@ -1,16 +0,0 @@
-// 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}},
-});
-
-}
-}

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

@@ -1,17 +0,0 @@
-// 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}},
-});
-
-}
-}

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

@@ -1,15 +0,0 @@
-// 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}},
-});
-
-}
-}

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

@@ -1,14 +0,0 @@
-// 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}},
-});
-
-}
-}

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

@@ -1,14 +0,0 @@
-// 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}},
-});
-
-}
-}

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

@@ -1,14 +0,0 @@
-// 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}},
-});
-
-}
-}

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

@@ -1,14 +0,0 @@
-// 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}},
-});
-
-}
-}

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

@@ -1,14 +0,0 @@
-// 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}},
-});
-
-}
-}

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

@@ -1,14 +0,0 @@
-// 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}},
-});
-
-}
-}

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

@@ -1,20 +0,0 @@
-// 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}},
-});
-
-}
-}

Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 8
polygon.mod/earcut/test/fixtures/issue35.cpp


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

@@ -1,15 +0,0 @@
-// 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}},
-});
-
-}
-}

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

@@ -1,18 +0,0 @@
-// 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}},
-});
-
-}
-}

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

@@ -1,15 +0,0 @@
-// 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}},
-});
-
-}
-}

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

@@ -1,15 +0,0 @@
-// 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}},
-});
-
-}
-}

Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 8
polygon.mod/earcut/test/fixtures/rain.cpp


Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 8
polygon.mod/earcut/test/fixtures/self_touching.cpp


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

@@ -1,13 +0,0 @@
-// 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}},
-});
-
-}
-}

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

@@ -1,14 +0,0 @@
-// 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}},
-});
-
-}
-}

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

@@ -1,17 +0,0 @@
-// 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}},
-});
-
-}
-}

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

@@ -1,14 +0,0 @@
-// 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}},
-});
-
-}
-}

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

@@ -1,15 +0,0 @@
-// 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}},
-});
-
-}
-}

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

@@ -1,17 +0,0 @@
-// 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}},
-});
-
-}
-}

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

@@ -1,20 +0,0 @@
-// 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}},
-});
-
-}
-}

Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 8
polygon.mod/earcut/test/fixtures/water.cpp


Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 8
polygon.mod/earcut/test/fixtures/water2.cpp


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

@@ -1,18 +0,0 @@
-// 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}},
-});
-
-}
-}

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

@@ -1,15 +0,0 @@
-// 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}},
-});
-
-}
-}

Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 8
polygon.mod/earcut/test/fixtures/water4.cpp


Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 8
polygon.mod/earcut/test/fixtures/water_huge.cpp


Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 8
polygon.mod/earcut/test/fixtures/water_huge2.cpp


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

@@ -1,70 +0,0 @@
-#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;
-}

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

@@ -1,27 +0,0 @@
-#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;
-};

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

@@ -1,117 +0,0 @@
-#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;
-}

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

@@ -1,510 +0,0 @@
-#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;
-}

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

@@ -1,15 +0,0 @@
-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

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

@@ -1,33 +0,0 @@
-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

+ 0 - 102
polygon.mod/glue.cpp

@@ -1,102 +0,0 @@
-/*
-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;
-}

+ 0 - 61
polygon.mod/polygon.bmx

@@ -1,61 +0,0 @@
-' 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

+ 0 - 203
quaternion.mod/doc/intro.bbdoc

@@ -1,203 +0,0 @@
-Quaternions are a mathematical concept that can be used to represent and manipulate 3D rotations in a
-way that avoids some of the issues encountered with other methods, such as Euler angles and rotation
-matrices. A quaternion is a four-dimensional complex number that can be written as `q = w + xi + yj + zk`,
-where `w`, `x`, `y`, and `z` are real numbers, and `i`, `j`, and `k` are imaginary units with the property
-`i^2 = j^2 = k^2 = ijk = -1`. In the context of 3D rotations, w represents the scalar (real) part,
-while `x`, `y`, and `z` together form the vector (imaginary) part.
-
-Quaternions have several advantages over other rotation representations:
-
-1. **Compactness** : Quaternions only require four numbers to represent a rotation, while rotation matrices
-require nine numbers.
-
-2. **Numerical stability** : Quaternions can be easily normalized, which helps maintain numerical stability
-during calculations.
-
-3. **Interpolation** : Quaternions can be smoothly interpolated using a method called Spherical Linear
-Interpolation (SLERP), which provides a more intuitive and accurate interpolation between rotations
-than other methods.
-
-4. **Avoiding Gimbal Lock** : Quaternions do not suffer from gimbal lock, a problem that occurs with
-Euler angles when two of the three axes of rotation become aligned, resulting in a loss of one degree
-of freedom. Gimbal lock is named after the mechanical gimbals used in early navigation systems,
-where the alignment of two gimbals would cause the system to lose its ability to rotate freely in
-all three dimensions. In the context of 3D rotations, gimbal lock can cause unexpected and undesirable
-behavior, such as sudden jumps in rotation or the inability to perform certain rotations. Quaternions,
-on the other hand, maintain their full range of motion without encountering gimbal lock, providing a
-more robust and reliable method for representing and manipulating 3D rotations.
-
-5. **Efficiency** : Quaternion operations, such as multiplication and inversion, are generally faster
-than their matrix counterparts.
-
-Despite these advantages, quaternions can be less intuitive to work with than Euler angles or rotation
-matrices, and some operations, like extracting a specific axis of rotation, can be more complicated.
-However, the benefits of using quaternions often outweigh these drawbacks, making them a popular choice
-for many 3D applications, including computer graphics and game development.
-
-In the following sections, we'll explore how to use the `SQuatD`, `SQuatF`, and `SQuatI` structs provided
-by the `brl.quaternion` module in BlitzMax to work with quaternions, and how they can be applied to various
-rotation tasks.
-
-### Euler Angles
-Euler angles are a set of three angles that describe the orientation of an object in
-three-dimensional space. They represent a sequence of three rotations applied around specific
-axes in a specified order. The angles are named after the Swiss mathematician Leonhard Euler,
-who extensively studied the topic.
-
-The three angles are often referred to as pitch, yaw, and roll:
-
-1. **Pitch (θ)** : The rotation around the X-axis. It represents the up and down tilting motion,like nodding your head.
-2. **Yaw (ψ)** : The rotation around the Y-axis. It represents the side-to-side turning motion, like shaking your head.
-3. **Roll (ϕ)** : The rotation around the Z-axis. It represents the side-to-side tilting motion, like tilting your head.
-
-Euler angles can be applied in different orders, such as XYZ, ZYX, or YXZ. The order in which the angles
-are applied matters because rotation is not commutative. For example, rotating around the X-axis and
-then the Y-axis will produce a different result than rotating around the Y-axis and then the X-axis.
-
-While Euler angles are intuitive and easy to understand, they can be problematic in certain situations,
-such as when they lead to gimbal lock. Quaternions are often used as an alternative to Euler angles
-because they avoid gimbal lock and provide smoother interpolation between rotations.
-
-## Basic Operations with Quaternions
-### Creating Quaternions
-To create a quaternion using the SQuatD class, you can use the constructor:
-
-```blitzmax
-Local quat:SQuatD = New SQuatD(x, y, z, w)
-```
-The `x`, `y`, `z`, and `w` parameters represent the components of the quaternion. You can also create a
-quaternion from Euler angles or a rotation matrix using the provided functions:
-
-```blitzmax
-Local euler:SVec3D = New SVec3D(pitch, yaw, roll)
-Local quatFromEuler:SQuatD = SQuatD.CreateFromEuler(euler)
-Local quatFromMatrix:SQuatD = SQuatD.CreateFromRotation(mat)
-```
-
-### Multiplying Quaternions
-To combine two rotations represented by quaternions, you can multiply them together:
-
-```blitzmax
-Local combinedQuat:SQuatD = quat1 * quat2
-```
-
-### Inverting Quaternions
-Inverting a quaternion refers to finding the quaternion that represents the opposite (or inverse) rotation.
-When you apply the inverse rotation to an object that has already been rotated by the original
-quaternion, the object returns to its initial orientation.
-
-A quaternion `q` is represented as `(x, y, z, w)`. Its inverse, denoted as `q_inv`,
-is calculated by taking the conjugate of `q` and then normalizing the result. The conjugate of a
-quaternion is obtained by negating the vector part (`x`, `y`, `z`) while keeping the scalar part
-(`w`) the same. The normalization is necessary to ensure that the inverse quaternion has a
-magnitude of 1, just like the original quaternion.
-
-Mathematically, the inverse quaternion `q_inv` is computed as follows:
-
-```
-q_inv = (x, y, z, w)^* / ||(x, y, z, w)||^2
-```
-where `^*` denotes the conjugate and `|| ||` denotes the magnitude (or norm) of the quaternion.
-
-In practice, you can use the `Invert()` method to find the inverse of a quaternion. Once you
-have the inverse quaternion, you can apply it to an object to undo the rotation applied by
-the original quaternion. This can be useful in situations where you need to reverse or cancel
-out a previous rotation.
-
-```blitzmax
-Local invertedQuat:SQuatD = quat.Invert()
-```
-
-### Interpolating Between Quaternions
-To smoothly interpolate between two rotations, you can use either linear interpolation
-(Interpolate) or spherical linear interpolation (SphericalInterpolate). The main difference
-between these two methods lies in how the interpolation is performed:
-
-1. **Linear Interpolation (Interpolate)** : Linear interpolation is the simplest method, where
-each component of the quaternion is linearly interpolated between the start and end values
-based on the interpolation factor. This method is faster to compute but can result in
-non-uniform motion and changes in speed during the interpolation. Linear interpolation does
-not guarantee that the resulting interpolated quaternions have a unit length, so it's necessary
-to normalize the result afterwards.
-
-2. **Spherical Linear Interpolation (SphericalInterpolate)** : Spherical linear interpolation,
-also known as *slerp*, takes into account the spherical geometry of quaternions. This method
-interpolates along the shortest path on the quaternion's hypersphere, resulting in uniform
-motion and constant speed during the interpolation. Slerp is more computationally expensive
-than linear interpolation, but it provides smoother and more natural transitions between rotations.
-Since slerp preserves the unit length of quaternions, there's no need to normalize the result.
-
-In summary, linear interpolation is faster but can produce non-uniform motion and requires
-normalization, while spherical linear interpolation provides smoother, more natural transitions
-at the cost of increased computational complexity.
-
-```blitzmax
-Local interpolatedQuat:SQuatD = quat1.Interpolate(quat2, t)
-Local slerpedQuat:SQuatD = quat1.SphericalInterpolate(quat2, t)
-```
-
-## Quaternion Applications
-Quaternions can be used to perform various operations on 3D rotations, such as converting between
-different rotation representations, rotating points or vectors, and generating rotation matrices.
-
-### Converting Between Representations
-To convert a quaternion to Euler angles:
-
-```blitzmax
-Local eulerAngles:SVec3D = quat.ToEuler()
-```
-
-To convert a quaternion to a rotation matrix:
-
-```blitzmax
-Local mat3:SMat3D = SQuatD.ToMat3(quat)
-Local mat4:SMat4D = SQuatD.ToMat4(quat)
-```
-
-### Rotating Points or Vectors
-To rotate a point or vector using a quaternion, you can first convert the quaternion to a
-rotation matrix and then apply the matrix to the point or vector. This approach is advantageous
-for several reasons:
-
-1. **Compatibility** : Converting a quaternion to a rotation matrix ensures compatibility with other
-systems or libraries that expect or require the use of matrices for transformations. This way, you
-can seamlessly integrate quaternion-based rotations with existing matrix-based systems.
-
-2. **Efficiency** : When you need to rotate multiple points or vectors, converting the quaternion to
-a rotation matrix first and then applying the matrix to each point or vector can be more efficient.
-This is because the conversion from quaternion to matrix is done once, and the resulting matrix can
-be reused for all the subsequent rotations.
-
-3. **Clarity** : For some users, working with matrices might be more intuitive or familiar than working
-with quaternions. By converting the quaternion to a rotation matrix, you can leverage the familiarity
-and ease of understanding associated with matrix-based transformations.
-
-```blitzmax
-Local rotationMatrix:SMat4D = SQuatD.ToMat4(quat)
-Local rotatedPoint:SVec3D = rotationMatrix * point
-```
-
-### Generating Rotation Matrices
-
-Quaternions can be used to generate rotation matrices that can be used in various transformations.
-Here are some examples:
-
-#### Applying Quaternion to a Matrix
-```blitzmax
-Local mat3:SMat3D = SQuatD.ToMat3(quat)
-Local mat4:SMat4D = SQuatD.ToMat4(quat)
-```
-
-#### Creating Translation and Rotation Matrix
-```blitzmax
-Local translation:SVec3D = New SVec3D(x, y, z)
-Local rotTransMatrix:SMat4D = SQuatD.RotTrans(quat, translation)
-```
-
-#### Creating Translation, Rotation, and Scaling Matrix
-```blitzmax
-Local translation:SVec3D = New SVec3D(x, y, z)
-Local scaling:SVec3D = New SVec3D(scaleX, scaleY, scaleZ)
-Local origin:SVec3D = New SVec3D(originX, originY, originZ)
-Local rotTransOriginMatrix:SMat4D = SQuatD.RotTransOrigin(quat, scaling, origin)
-```

+ 0 - 2
quaternion.mod/glue.h

@@ -1,2 +0,0 @@
-#include <math.h>
-

+ 0 - 1445
quaternion.mod/quaternion.bmx

@@ -1,1445 +0,0 @@
-' Copyright (c) 2020 Bruce A Henderson
-'
-' This software is provided 'as-is', without any express or implied
-' warranty. In no event will the authors be held liable for any damages
-' arising from the use of this software.
-' 
-' Permission is granted to anyone to use this software for any purpose,
-' including commercial applications, and to alter it and redistribute it
-' freely, subject to the following restrictions:
-' 
-'    1. The origin of this software must not be misrepresented; you must not
-'    claim that you wrote the original software. If you use this software
-'    in a product, an acknowledgment in the product documentation would be
-'    appreciated but is not required.
-' 
-'    2. Altered source versions must be plainly marked as such, and must not be
-'    misrepresented as being the original software.
-' 
-'    3. This notice may not be removed or altered from any source
-'    distribution.
-' 
-SuperStrict
-
-Rem
-bbdoc: Math/Quaternion
-End Rem
-Module BRL.Quaternion
-
-ModuleInfo "Version: 1.02"
-ModuleInfo "Author: Bruce A Henderson"
-ModuleInfo "License: zlib"
-ModuleInfo "Copyright: 2020 Bruce A Henderson"
-
-ModuleInfo "History: 1.02"
-ModuleInfo "History: Fixed Normal()."
-ModuleInfo "History: Refactored Euler methods. Added rotation enum."
-ModuleInfo "History: 1.01"
-ModuleInfo "History: Fixed Euler conversions."
-ModuleInfo "History: 1.00"
-ModuleInfo "History: Initial Release"
-
-Import BRL.Math
-Import BRL.Matrix
-Import BRL.StringBuilder
-
-Import "glue.h"
-
-Rem
-bbdoc: A Quaternion.
-about: Quaternions are used to represent rotations.
-They are compact, don't suffer from gimbal lock and can easily be interpolated.
-End Rem
-Struct SQuatD
-	Field x:Double
-	Field y:Double
-	Field z:Double
-	Field w:Double
-	
-	Method New()
-		Self.w = 1
-	End Method
-	
-	Rem
-	bbdoc: Creates a new #SQuatD from the supplied arguments.
-	End Rem
-	Method New(x:Double, y:Double, z:Double, w:Double = 1)
-		Self.x = x
-		Self.y = y
-		Self.z = z
-		Self.w = w
-	End Method
-	
-	Rem
-	bbdoc: Creates a new #SQuatD from the rotation specified by the @euler angle and @order.
-	End Rem
-	Function CreateFromEuler:SQuatD(euler:SVec3D, order:ERotationOrder = ERotationOrder.XYZ)
-		Return New SQuatD.EulerRotate(euler, order)
-	End Function
-	
-	Rem
-	bbdoc: Creates a new #SQuatD from the rotation component of matrix @mat.
-	about: see http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
-	End Rem
-	Function CreateFromRotation:SQuatD(mat:SMat4D)
-		Local trace:Double = mat.a + mat.f + mat.k
-		
-		If trace > 0 Then
-		
-			Local s:Double = 0.5 / Sqr( trace + 1 )
-			
-			Return New SQuatD((mat.g - mat.j) * s, (mat.i - mat.c) * s, (mat.b - mat.e) * s, 0.25 / s)
-		
-		Else If mat.a > mat.f And mat.a > mat.k Then
-		
-			Local s:Double = 2.0 * Sqr(1.0 + mat.a - mat.f - mat.k)
-			
-			Return New SQuatD(0.25 * s, (mat.e + mat.b) / s, (mat.i + mat.c) / s, (mat.g - mat.j) / s)
-		
-		Else If mat.f > mat.k Then
-		
-			Local s:Double = 2.0 * Sqr(1.0 + mat.f - mat.a - mat.k)
-		
-			Return New SQuatD((mat.e + mat.b) / s, 0.25 * s, (mat.j + mat.g) / s, (mat.i - mat.c) / s)
-		
-		Else
-		
-			Local s:Double = 2.0 * Sqr( 1.0 + mat.k - mat.a - mat.f )
-		
-			Return New SQuatD((mat.i + mat.c) / s, (mat.j + mat.g) / s, 0.25 * s, (mat.b - mat.e) / s)
-			
-		End If
-		
-	End Function
-
-	Rem
-	bbdoc: Applies the quaternion @a to the matrix, returning a new matrix.
-	End Rem
-	Function ToMat3:SMat3D(a:SQuatD)
-		Local ax:Double = a.x
-		Local ay:Double = a.y
-		Local az:Double = a.z
-		Local aw:Double = a.w
-		Local ax2:Double = ax + ax
-		Local ay2:Double = ay + ay
-		Local az2:Double = az + az
-		Local axx:Double = ax * ax2
-		Local ayx:Double = ay * ax2
-		Local ayy:Double = ay * ay2
-		Local azx:Double = az * ax2
-		Local azy:Double = az * ay2
-		Local azz:Double = az * az2
-		Local awx:Double = aw * ax2
-		Local awy:Double = aw * ay2
-		Local awz:Double = aw * az2
-		Return New SMat3D(1 - (ayy + azz), ayx + awz, azx - awy, ayx - awz, 1.0 - (axx + azz), azy + awx, azx + awy, azy - awx, 1.0 - (axx + ayy))
-	End Function
-
-	Rem
-	bbdoc: Applies the quaternian to the matrix, return the new matrix.
-	End Rem
-	Function ToMat4:SMat4D(a:SQuatD)
-		Local ax:Double = a.x
-		Local ay:Double = a.y
-		Local az:Double = a.z
-		Local aw:Double = a.w
-		Local ax2:Double = ax + ax
-		Local ay2:Double = ay + ay
-		Local az2:Double = az + az
-		Local axx:Double = ax * ax2
-		Local ayx:Double = ay * ax2
-		Local ayy:Double = ay * ay2
-		Local azx:Double = az * ax2
-		Local azy:Double = az * ay2
-		Local azz:Double = az * az2
-		Local awx:Double = aw * ax2
-		Local awy:Double = aw * ay2
-		Local awz:Double = aw * az2
-		Return New SMat4D(1.0 - (ayy + azz), ayx + awz, azx - awy, 0, ..
-			ayx - awz, 1.0 - (axx + azz), azy + awx, 0, ..
-			azx + awy, azy - awx, 1.0 - (axx + ayy), 0, ..
-			0, 0, 0, 1)
-	End Function	
-
-	Rem
-	bbdoc: Creates a translation and rotation matrix.
-	about: The returned matrix is such that it places objects at position @s, oriented in rotation @a.
-	End Rem
-	Function RotTrans:SMat4D(a:SQuatD, s:SVec3D)
-		Local ax:Double = a.x
-		Local ay:Double = a.y
-		Local az:Double = a.x
-		Local aw:Double = a.w
-		Local ax2:Double = ax + ax
-		Local ay2:Double = ay + ay
-		Local az2:Double = az + az
-		Local axx:Double = ax * ax2
-		Local axy:Double = ax * ay2
-		Local axz:Double = ax * az2
-		Local ayy:Double = ay * ay2
-		Local ayz:Double = ay * az2
-		Local azz:Double = az * az2
-		Local awx:Double = aw * ax2
-		Local awy:Double = aw * ay2
-		Local awz:Double = aw * az2
-		Return New SMat4D(1.0 - (ayy + azz), axy + awz, axz - awy, 0, ..
-			axy - awz, 1.0 - (axx + azz), ayz + awx, 0, ..
-			axz + awy, ayz - awx, 1.0 - (axx + ayy), 0, ..
-			s.x, s.y, s.z, 1)
-	End Function
-	
-	Rem
-	bbdoc: Creates a translation, rotation and scaling matrix.
-	about: The returned matrix is such that it places objects at position @origin, oriented in rotation @a and scaled by @s.
-	End Rem
-	Function RotTransOrigin:SMat4D(a:SQuatD, s:SVec3D, origin:SVec3D)
-		Local ax:Double = a.x
-		Local ay:Double = a.y
-		Local az:Double = a.x
-		Local aw:Double = a.w
-		Local ax2:Double = ax + ax
-		Local ay2:Double = ay + ay
-		Local az2:Double = az + az
-		Local axx:Double = ax * ax2
-		Local axy:Double = ax * ay2
-		Local axz:Double = ax * az2
-		Local ayy:Double = ay * ay2
-		Local ayz:Double = ay * az2
-		Local azz:Double = az * az2
-		Local awx:Double = aw * ax2
-		Local awy:Double = aw * ay2
-		Local awz:Double = aw * az2
-		Local ox:Double = origin.x
-		Local oy:Double = origin.y
-		Local oz:Double = origin.z
-		Local o00:Double = 1.0 - (ayy + azz)
-		Local o01:Double = axy + awz
-		Local o02:Double = axz - awy
-		Local o10:Double = axy - awz
-		Local o11:Double = 1.0 - (axx + azz)
-		Local o12:Double = ayz + awx
-		Local o20:Double = axz + awy
-		Local o21:Double = ayz - awx
-		Local o22:Double = 1.0 - (axx + ayy)
-		Return New SMat4D(o00, o01, o02, 0, ..
-			o10, o11, o12, 0, ..
-			o20, o21, o22, 0, ..
-			s.x + ox - (o00 * ox + o10 * oy + o20 * oz), ..
-			s.y + oy - (o01 * ox + o11 * oy + o21 * oz), ..
-			s.z + oz - (o02 * ox + o12 * oy + o22 * oz), 1)
-	End Function
-
-	Rem
-	bbdoc: Returns the angle between ths quaternion and the quaternion @quat.
-	End Rem
-	Method AngleTo:Double(quat:SQuatD)
-		Local d:Double = Max(-1, Min( 1, dot(quat)))
-		Return 114.591559026:Double * _acos(Abs(d))
-	End Method
-	
-	Rem
-	bbdoc: The dot product between two rotations.
-	End Rem
-	Method Dot:Double(b:SQuatD)
-		Return x * b.x + y * b.y + z * b.z + w * b.w
-	End Method
-	
-	Rem
-	bbdoc: Returns the Inverse of rotation.
-	End Rem
-	Method Invert:SQuatD()
-		Local dot:Double = x * x + y * y + z * z + w * w
-		Local invdot:Double
-		If dot <> 0 Then
-			invdot = 1 / dot
-		End If
-		Return New SQuatD(-x * invdot, -y * invdot, -z * invdot, w * invdot)
-	End Method
-	
-	Rem
-	bbdoc: Interpolates between the SQuatD and @b by @t and normalizes the result afterwards.
-	End Rem
-	Method Interpolate:SQuatD(b:SQuatD, t:Double)
-		Return New SQuatD(Lerp(x, b.x, t), Lerp(y, b.y, t), Lerp(z, b.z, t), Lerp(w, b.w, t))
-	End Method
-	
-	Rem
-	bbdoc: Computes the length of this quaternion, considered as a 4 dimensional vector.
-	End Rem
-	Method Length:Double()
-		Return Sqr(x * x + y * y + z * z + w * w)
-	End Method
-	
-	Rem
-	bbdoc: Computes the length of this quaternion, considered as a 4 dimensional vector.
-	about: Calculating the squared length instead of the length is much faster.
-	Often if you are comparing lengths of two quaternions you can just compare their squared lengths.
-	End Rem
-	Method LengthSquared:Double()
-		Return x * x + y * y + z * z + w * w
-	End Method
-	
-	Rem
-	bbdoc: Multiplies the quaternion by @b, returning a new quaternion.
-	End Rem
-	Method Operator*:SQuatD(b:SQuatD)
-		Return New SQuatD(x * b.w + w * b.x + y * b.z - z * b.y, ..
-			y * b.w + w * b.y + z * b.x - x * b.z, ..
-			z * b.w + w * b.z + x * b.y - y * b.x, ..
-			w * b.w - x * b.x - y * b.y - z * b.z)
-	End Method
-	
-	Rem
-	bbdoc: Returns a new quaternion, negated.
-	End Rem
-	Method Operator-:SQuatD()
-		Return New SQuatD(-x, -y, -z, -w)
-	End Method
-	
-	Rem
-	bbdoc: The identity rotation.
-	End Rem
-	Function Identity:SQuatD()
-		Return New SQuatD(0, 0, 0, 1)
-	End Function
-	
-	Rem
-	bbdoc: Converts this quaternion to one with the same orientation but with a magnitude of 1.
-	End Rem
-	Method Normal:SQuatD()
-		Local length:Double = x * x + y * y + z * z + w * w
-		If length = 0 Then
-			Return New SQuatD(0, 0, 0, 1)
-		Else
-			length = 1 / Sqr(length)
-			Return New SQuatD(x * length, y * length, z * length, w * length)
-		End If
-	End Method
-	
-	Rem
-	bbdoc: Rotates this quaternion by a given angular step @s to the specified quaternion @quat.
-	End Rem
-	Method RotateTowards:SQuatD(quat:SQuatD, s:Double)
-		Local angle:Double = AngleTo(quat)
-		
-		If angle = 0 Then
-			Return Self
-		End If
-		
-		Local t:Double = Min(1, s / angle)
-		
-		Return SphericalInterpolate(quat, t)
-	End Method
-	
-	Rem
-	bbdoc: Spherically interpolates between this SQuatD and @b by @t.
-	End Rem
-	Method SphericalInterpolate:SQuatD(b:SQuatD, t:Double)
-		Local bx:Double = b.x
-		Local by:Double = b.y
-		Local bz:Double = b.z
-		Local bw:Double = b.w
-		Local scale0:Double
-		Local scale1:Double
-
-		Local cosom:Double = x * bx + y * by + z * bz + w * bw
-
-		If cosom < 0 Then
-			cosom = -cosom
-			bx = -bx
-			by = -by
-			bz = -bz
-			bw = -bw
-		End If
-		
-		If 1 - cosom > 0.000001 Then
-			Local omega:Double = _acos(cosom)
-			Local sinom:Double = _sin(omega)
-			scale0 = _sin((1.0 - t) * omega) / sinom
-			scale1 = _sin(t * omega) / sinom
-		Else
-			scale0 = 1 - t
-			scale1 = t
-		End If
-		
-		Return New SQuatD(scale0 * x + scale1 * bx, scale0 * y + scale1 * by, scale0 * z + scale1 * bz, scale0 * w + scale1 * bw)
-	End Method
-
-	Rem
-	bbdoc: Returns a rotation that rotates around @rot.
-	End Rem
-	Method EulerRotate:SQuatD(rot:SVec3D, order:ERotationOrder = ERotationOrder.XYZ)
-		Local cx:Double = Cos(rot.x * .5)
-		Local cy:Double = Cos(rot.y * .5)
-		Local cz:Double = Cos(rot.z * .5)
-		Local sx:Double = Sin(rot.x * .5)
-		Local sy:Double = Sin(rot.y * .5)
-		Local sz:Double = Sin(rot.z * .5)
-
-		Select order
-			Case ERotationOrder.XYZ
-				Return New SQuatD(sx * cy * cz + cx * sy * sz, ..
-					cx * sy * cz - sx * cy * sz, ..
-					cx * cy * sz + sx * sy * cz, ..
-					cx * cy * cz - sx * sy * sz)
-			Case ERotationOrder.XZY
-				Return New SQuatD(sx * cy * cz - cx * sy * sz, ..
-					cx * sy * cz - sx * cy * sz, ..
-					cx * cy * sz + sx * sy * cz, ..
-					cx * cy * cz + sx * sy * sz)
-			Case ERotationOrder.YXZ
-				Return New SQuatD(sx * cy * cz + cx * sy * sz, ..
-					cx * sy * cz - sx * cy * sz, ..
-					cx * cy * sz - sx * sy * cz, ..
-					cx * cy * cz + sx * sy * sz)
-			Case ERotationOrder.YZX
-				Return New SQuatD(sx * cy * cz + cx * sy * sz, ..
-					cx * sy * cz + sx * cy * sz, ..
-					cx * cy * sz - sx * sy * cz, ..
-					cx * cy * cz - sx * sy * sz)
-			Case ERotationOrder.ZXY
-				Return New SQuatD(sx * cy * cz - cx * sy * sz, ..
-					cx * sy * cz + sx * cy * sz, ..
-					cx * cy * sz + sx * sy * cz, ..
-					cx * cy * cz - sx * sy * sz)
-			Case ERotationOrder.ZYX
-				Return New SQuatD(sx * cy * cz - cx * sy * sz, ..
-					cx * sy * cz + sx * cy * sz, ..
-					cx * cy * sz - sx * sy * cz, ..
-					cx * cy * cz + sx * sy * sz)
-		End Select
-	End Method
-
-	Rem
-	bbdoc: Returns the quaternion converted to Euler angles, using the specified rotation @order.
-	End Rem
-	Method ToEuler:SVec3D(order:ERotationOrder = ERotationOrder.XYZ)
-		Local q:SQuatD = Normal()
-		Local mat:SMat4D = ToMat4(q)
-		
-		Local x:Double
-		Local y:Double
-		Local z:Double
-
-		Select order
-			Case ERotationOrder.XYZ
-				y = ASin( Max( -1, Min( 1, mat.i ) ) )
-		
-				If Abs(mat.i) < 0.9999999 Then
-					x = ATan2( -mat.j, mat.k )
-					z = ATan2( -mat.e, mat.a )
-				Else
-					x = ATan2( mat.g, mat.f )
-				End If
-				
-			Case ERotationOrder.XZY
-				z = ASin( -Max( -1, Min( 1, mat.e ) ) )
-
-				If Abs(mat.e) < 0.9999999 Then
-					x = ATan2( mat.g, mat.f )
-					y = ATan2( mat.i, mat.a )
-				Else
-					x = ATan2( -mat.j, mat.k )
-				End If
-
-			Case ERotationOrder.YXZ
-				x = ASin( -Max( -1, Min( 1, mat.j ) ) )
-		
-				If Abs(mat.j) < 0.9999999 Then
-					y = ATan2( mat.i, mat.k )
-					z = ATan2( mat.b, mat.f )
-				Else
-					y = ATan2( -mat.c, mat.a )
-				End If
-			
-			Case ERotationOrder.YZX
-				z = ASin( Max( -1, Min( 1, mat.b ) ) )
-		
-				If Abs(mat.b) < 0.9999999 Then
-					x = ATan2( -mat.j, mat.f )
-					y = ATan2( -mat.c, mat.a )
-				Else
-					y = ATan2( mat.i, mat.k )
-				End If
-			
-			Case ERotationOrder.ZXY
-				x = ASin( Max( -1, Min( 1, mat.g ) ) )
-		
-				If Abs(mat.g) < 0.9999999 Then
-					y = ATan2( -mat.c, mat.k )
-					z = ATan2( -mat.e, mat.f )
-				Else
-					z = ATan2( mat.b, mat.a )
-				End If
-			
-			Case ERotationOrder.ZYX
-				y = ASin( -Max( -1, Min( 1, mat.c ) ) )
-		
-				If Abs(mat.c) < 0.9999999 Then
-					x = ATan2( mat.g, mat.k )
-					z = ATan2( mat.b, mat.a )
-				Else
-					z = ATan2( -mat.e, mat.f )
-				End If
-			
-		End Select
-		
-		Return New SVec3D(x, y, z)
-	End Method
-
-	Rem
-	bbdoc: Returns a #String representation of the quaternion.
-	End Rem
-	Method ToString:String() Override
-		Local sb:TStringBuilder = New TStringBuilder
-		
-		sb.Append("(").Append(x).Append(", ").Append(y).Append(", ").Append(z).Append(", ").Append(w).Append(")")
-		
-		Return sb.ToString()
-	End Method
-
-End Struct
-
-Rem
-bbdoc: A #Float backed Quaternion.
-about: Quaternions are used to represent rotations.
-They are compact, don't suffer from gimbal lock and can easily be interpolated.
-End Rem
-Struct SQuatF
-	Field x:Float
-	Field y:Float
-	Field z:Float
-	Field w:Float
-
-	Method New()
-		Self.w = 1
-	End Method
-
-	Rem
-	bbdoc: Creates a new #SQuatF from the supplied arguments.
-	End Rem
-	Method New(x:Float, y:Float, z:Float, w:Float)
-		Self.x = x
-		Self.y = y
-		Self.z = z
-		Self.w = w
-	End Method
-
-	Rem
-	bbdoc: Creates a new #SQuatF from the rotation specified by the @euler angle and @order.
-	End Rem
-	Function CreateFromEuler:SQuatF(euler:SVec3F, order:ERotationOrder = ERotationOrder.XYZ)
-		Return New SQuatF.EulerRotate(euler, order)
-	End Function
-	
-	Rem
-	bbdoc: Creates a new #SQuatD from the rotation component of matrix @mat.
-	about: see http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
-	End Rem
-	Function CreateFromRotation:SQuatF(mat:SMat4F)
-		Local trace:Float = mat.a + mat.f + mat.k
-		
-		If trace > 0 Then
-		
-			Local s:Float = 0.5 / Sqr( trace + 1 )
-			
-			Return New SQuatF((mat.g - mat.j) * s, (mat.i - mat.c) * s, (mat.b - mat.e) * s, 0.25 / s)
-		
-		Else If mat.a > mat.f And mat.a > mat.k Then
-		
-			Local s:Float = 2.0 * Sqr(1.0 + mat.a - mat.f - mat.k)
-			
-			Return New SQuatF(0.25 * s, (mat.e + mat.b) / s, (mat.i + mat.c) / s, (mat.g - mat.j) / s)
-		
-		Else If mat.f > mat.k Then
-		
-			Local s:Float = 2.0 * Sqr(1.0 + mat.f - mat.a - mat.k)
-		
-			Return New SQuatF((mat.e + mat.b) / s, 0.25 * s, (mat.j + mat.g) / s, (mat.i - mat.c) / s)
-		
-		Else
-		
-			Local s:Float = 2.0 * Sqr( 1.0 + mat.k - mat.a - mat.f )
-		
-			Return New SQuatF((mat.i + mat.c) / s, (mat.j + mat.g) / s, 0.25 * s, (mat.b - mat.e) / s)
-			
-		End If
-		
-	End Function
-
-	Rem
-	bbdoc: Applies the quaternion @a to the matrix, returning a new matrix.
-	End Rem
-	Function ToMat3:SMat3F(a:SQuatF)
-		Local ax:Float = a.x
-		Local ay:Float = a.y
-		Local az:Float = a.z
-		Local aw:Float = a.w
-		Local ax2:Float = ax + ax
-		Local ay2:Float = ay + ay
-		Local az2:Float = az + az
-		Local axx:Float = ax * ax2
-		Local ayx:Float = ay * ax2
-		Local ayy:Float = ay * ay2
-		Local azx:Float = az * ax2
-		Local azy:Float = az * ay2
-		Local azz:Float = az * az2
-		Local awx:Float = aw * ax2
-		Local awy:Float = aw * ay2
-		Local awz:Float = aw * az2
-		Return New SMat3F(1 - ayy - azz, ayx + awz, azx - awy, ayx - awz, 1.0 - axx - azz, azy + awx, azx + awy, azy - awx, 1.0 - axx - ayy)
-	End Function
-
-	Rem
-	bbdoc: Applies the quaternian to the matrix, return the new matrix.
-	End Rem
-	Function ToMat4:SMat4F(a:SQuatF)
-		Local ax:Float = a.x
-		Local ay:Float = a.y
-		Local az:Float = a.z
-		Local aw:Float = a.w
-		Local ax2:Float = ax + ax
-		Local ay2:Float = ay + ay
-		Local az2:Float = az + az
-		Local axx:Float = ax * ax2
-		Local ayx:Float = ay * ax2
-		Local ayy:Float = ay * ay2
-		Local azx:Float = az * ax2
-		Local azy:Float = az * ay2
-		Local azz:Float = az * az2
-		Local awx:Float = aw * ax2
-		Local awy:Float = aw * ay2
-		Local awz:Float = aw * az2
-		Return New SMat4F(1.0 - (ayy + azz), ayx + awz, azx - awy, 0, ..
-			ayx - awz, 1.0 - (axx + azz), azy + awx, 0, ..
-			azx + awy, azy - awx, 1.0 - (axx + ayy), 0, ..
-			0, 0, 0, 1)
-	End Function	
-
-	Rem
-	bbdoc: Creates a translation and rotation matrix.
-	about: The returned matrix is such that it places objects at position @s, oriented in rotation @a.
-	End Rem
-	Function RotTrans:SMat4F(a:SQuatF, s:SVec3F)
-		Local ax:Float = a.x
-		Local ay:Float = a.y
-		Local az:Float = a.x
-		Local aw:Float = a.w
-		Local ax2:Float = ax + ax
-		Local ay2:Float = ay + ay
-		Local az2:Float = az + az
-		Local axx:Float = ax * ax2
-		Local axy:Float = ax * ay2
-		Local axz:Float = ax * az2
-		Local ayy:Float = ay * ay2
-		Local ayz:Float = ay * az2
-		Local azz:Float = az * az2
-		Local awx:Float = aw * ax2
-		Local awy:Float = aw * ay2
-		Local awz:Float = aw * az2
-		Return New SMat4F(1.0 - ayy - azz, axy + awz, axz - awy, 0, ..
-			axy - awz, 1.0 - axx - azz, ayz + awx, 0, ..
-			axz + awy, ayz - awx, 1.0 - axx - ayy, 0, ..
-			s.x, s.y, s.z, 1)
-	End Function
-	
-	Rem
-	bbdoc: Creates a translation, rotation and scaling matrix.
-	about: The returned matrix is such that it places objects at position @origin, oriented in rotation @a and scaled by @s.
-	End Rem
-	Function RotTransOrigin:SMat4F(a:SQuatF, s:SVec3F, origin:SVec3F)
-		Local ax:Float = a.x
-		Local ay:Float = a.y
-		Local az:Float = a.x
-		Local aw:Float = a.w
-		Local ax2:Float = ax + ax
-		Local ay2:Float = ay + ay
-		Local az2:Float = az + az
-		Local axx:Float = ax * ax2
-		Local axy:Float = ax * ay2
-		Local axz:Float = ax * az2
-		Local ayy:Float = ay * ay2
-		Local ayz:Float = ay * az2
-		Local azz:Float = az * az2
-		Local awx:Float = aw * ax2
-		Local awy:Float = aw * ay2
-		Local awz:Float = aw * az2
-		Local ox:Float = origin.x
-		Local oy:Float = origin.y
-		Local oz:Float = origin.z
-		Local o00:Float = 1.0 - ayy - azz
-		Local o01:Float = axy + awz
-		Local o02:Float = axz - awy
-		Local o10:Float = axy - awz
-		Local o11:Float = 1.0 - axx - azz
-		Local o12:Float = ayz + awx
-		Local o20:Float = axz + awy
-		Local o21:Float = ayz - awx
-		Local o22:Float = 1.0 - axx - ayy
-		Return New SMat4F(o00, o01, o02, 0, ..
-			o10, o11, o12, 0, ..
-			o20, o21, o22, 0, ..
-			s.x + ox - (o00 * ox + o10 * oy + o20 * oz), ..
-			s.y + oy - (o01 * ox + o11 * oy + o21 * oz), ..
-			s.z + oz - (o02 * ox + o12 * oy + o22 * oz), 1)
-	End Function
-
-	Rem
-	bbdoc: Returns the angle between ths quaternion and the quaternion @quat.
-	End Rem
-	Method AngleTo:Float(quat:SQuatF)
-		Local d:Float = Max(-1, Min( 1, dot(quat)))
-		Return 114.591559026 * _acos(Abs(d))
-	End Method
-
-	Rem
-	bbdoc: The dot product between two rotations.
-	End Rem
-	Method Dot:Float(b:SQuatF)
-		Return x * b.x + y * b.y + z * b.z + w * b.w
-	End Method
-	
-	Rem
-	bbdoc: Returns the Inverse of rotation.
-	End Rem
-	Method Invert:SQuatF()
-		Local dot:Float = x * x + y * y + z * z + w * w
-		Local invdot:Float
-		If dot <> 0 Then
-			invdot = 1 / dot
-		End If
-		Return New SQuatF(-x * invdot, -y * invdot, -z * invdot, w * invdot)
-	End Method
-	
-	Rem
-	bbdoc: Interpolates between the SQuatF and @b by @t and normalizes the result afterwards.
-	End Rem
-	Method Interpolate:SQuatF(b:SQuatF, t:Float)
-		Return New SQuatF(LerpF(x, b.x, t), LerpF(y, b.y, t), LerpF(z, b.z, t), LerpF(w, b.w, t))
-	End Method
-
-	Rem
-	bbdoc: Computes the length of this quaternion, considered as a 4 dimensional vector.
-	End Rem
-	Method Length:Float()
-		Return Sqr(x * x + y * y + z * z + w * w)
-	End Method
-	
-	Rem
-	bbdoc: Computes the length of this quaternion, considered as a 4 dimensional vector.
-	about: Calculating the squared length instead of the length is much faster.
-	Often if you are comparing lengths of two quaternions you can just compare their squared lengths.
-	End Rem
-	Method LengthSquared:Float()
-		Return x * x + y * y + z * z + w * w
-	End Method
-
-	Rem
-	bbdoc: Multiplies the quaternion by @b, returning a new quaternion.
-	End Rem
-	Method Operator*:SQuatF(b:SQuatF)
-		Return New SQuatF(x * b.w + w * b.x + y * b.z - z * b.y, ..
-			y * b.w + w * b.y + z * b.x - x * b.z, ..
-			z * b.w + w * b.z + x * b.y - y * b.x, ..
-			w * b.w - x * b.x - y * b.y - z * b.z)
-	End Method
-	
-	Rem
-	bbdoc: Returns a new quaternion, negated.
-	End Rem
-	Method Operator-:SQuatF()
-		Return New SQuatF(-x, -y, -z, -w)
-	End Method
-	
-	Rem
-	bbdoc: The identity rotation.
-	End Rem
-	Function Identity:SQuatF()
-		Return New SQuatF(0, 0, 0, 1)
-	End Function
-	
-	Rem
-	bbdoc: Converts this quaternion to one with the same orientation but with a magnitude of 1.
-	End Rem
-	Method Normal:SQuatF()
-		Local length:Float = x * x + y * y + z * z + w * w
-		If length = 0 Then
-			Return New SQuatF(0, 0, 0, 1)
-		Else
-			length = 1 / Sqr(length)
-
-			Return New SQuatF(x * length, y * length, z * length, w * length)
-		End If
-	End Method
-
-	Rem
-	bbdoc: Rotates this quaternion by a given angular step @s to the specified quaternion @quat.
-	End Rem
-	Method RotateTowards:SQuatF(quat:SQuatF, s:Float)
-		Local angle:Float = AngleTo(quat)
-		
-		If angle = 0 Then
-			Return Self
-		End If
-		
-		Local t:Float = Min(1, s / angle)
-		
-		Return SphericalInterpolate(quat, t)
-	End Method
-
-	Rem
-	bbdoc: Spherically interpolates between this SQuatF and @b by @t.
-	End Rem
-	Method SphericalInterpolate:SQuatF(b:SQuatF, t:Float)
-		Local bx:Float = b.x
-		Local by:Float = b.y
-		Local bz:Float = b.z
-		Local bw:Float = b.w
-		Local scale0:Float
-		Local scale1:Float
-
-		Local cosom:Float = x * bx + y * by + z * bz + w * bw
-
-		If cosom < 0 Then
-			cosom = -cosom
-			bx = -bx
-			by = -by
-			bz = -bz
-			bw = -bw
-		End If
-		
-		If 1 - cosom > 0.000001 Then
-			Local omega:Float = _acos(cosom)
-			Local sinom:Float = _sin(omega)
-			scale0 = _sin((1.0 - t) * omega) / sinom
-			scale1 = _sin(t * omega) / sinom
-		Else
-			scale0 = 1 - t
-			scale1 = t
-		End If
-		
-		Return New SQuatF(scale0 * x + scale1 * bx, scale0 * y + scale1 * by, scale0 * z + scale1 * bz, scale0 * w + scale1 * bw)
-	End Method
-	
-	Rem
-	bbdoc: Returns a rotation that rotates around @rot.
-	End Rem
-	Method EulerRotate:SQuatF(rot:SVec3F, order:ERotationOrder = ERotationOrder.XYZ)
-		Local cx:Float = Cos(rot.x * .5)
-		Local cy:Float = Cos(rot.y * .5)
-		Local cz:Float = Cos(rot.z * .5)
-		Local sx:Float = Sin(rot.x * .5)
-		Local sy:Float = Sin(rot.y * .5)
-		Local sz:Float = Sin(rot.z * .5)
-		
-		Select order
-			Case ERotationOrder.XYZ
-				Return New SQuatF(sx * cy * cz + cx * sy * sz, ..
-					cx * sy * cz - sx * cy * sz, ..
-					cx * cy * sz + sx * sy * cz, ..
-					cx * cy * cz - sx * sy * sz)
-			Case ERotationOrder.XZY
-				Return New SQuatF(sx * cy * cz - cx * sy * sz, ..
-					cx * sy * cz - sx * cy * sz, ..
-					cx * cy * sz + sx * sy * cz, ..
-					cx * cy * cz + sx * sy * sz)
-			Case ERotationOrder.YXZ
-				Return New SQuatF(sx * cy * cz + cx * sy * sz, ..
-					cx * sy * cz - sx * cy * sz, ..
-					cx * cy * sz - sx * sy * cz, ..
-					cx * cy * cz + sx * sy * sz)
-			Case ERotationOrder.YZX
-				Return New SQuatF(sx * cy * cz + cx * sy * sz, ..
-					cx * sy * cz + sx * cy * sz, ..
-					cx * cy * sz - sx * sy * cz, ..
-					cx * cy * cz - sx * sy * sz)
-			Case ERotationOrder.ZXY
-				Return New SQuatF(sx * cy * cz - cx * sy * sz, ..
-					cx * sy * cz + sx * cy * sz, ..
-					cx * cy * sz + sx * sy * cz, ..
-					cx * cy * cz - sx * sy * sz)
-			Case ERotationOrder.ZYX
-				Return New SQuatF(sx * cy * cz - cx * sy * sz, ..
-					cx * sy * cz + sx * cy * sz, ..
-					cx * cy * sz - sx * sy * cz, ..
-					cx * cy * cz + sx * sy * sz)
-		End Select
-	End Method
-
-	Rem
-	bbdoc: Returns the quaternion converted to Euler angles, using the specified rotation @order.
-	End Rem
-	Method ToEuler:SVec3F(order:ERotationOrder = ERotationOrder.XYZ)
-		Local q:SQuatF = Normal()
-		Local mat:SMat4F = ToMat4(q)
-		
-		Local x:Float
-		Local y:Float
-		Local z:Float
-
-		Select order
-			Case ERotationOrder.XYZ
-				y = ASin( Max( -1, Min( 1, mat.i ) ) )
-		
-				If Abs(mat.i) < 0.9999999 Then
-					x = ATan2( -mat.j, mat.k )
-					z = ATan2( -mat.e, mat.a )
-				Else
-					x = ATan2( mat.g, mat.f )
-				End If
-				
-			Case ERotationOrder.XZY
-				z = ASin( -Max( -1, Min( 1, mat.e ) ) )
-
-				If Abs(mat.e) < 0.9999999 Then
-					x = ATan2( mat.g, mat.f )
-					y = ATan2( mat.i, mat.a )
-				Else
-					x = ATan2( -mat.j, mat.k )
-				End If
-
-			Case ERotationOrder.YXZ
-				x = ASin( -Max( -1, Min( 1, mat.j ) ) )
-		
-				If Abs(mat.j) < 0.9999999 Then
-					y = ATan2( mat.i, mat.k )
-					z = ATan2( mat.b, mat.f )
-				Else
-					y = ATan2( -mat.c, mat.a )
-				End If
-			
-			Case ERotationOrder.YZX
-				z = ASin( Max( -1, Min( 1, mat.b ) ) )
-		
-				If Abs(mat.b) < 0.9999999 Then
-					x = ATan2( -mat.j, mat.f )
-					y = ATan2( -mat.c, mat.a )
-				Else
-					y = ATan2( mat.i, mat.k )
-				End If
-			
-			Case ERotationOrder.ZXY
-				x = ASin( Max( -1, Min( 1, mat.g ) ) )
-		
-				If Abs(mat.g) < 0.9999999 Then
-					y = ATan2( -mat.c, mat.k )
-					z = ATan2( -mat.e, mat.f )
-				Else
-					z = ATan2( mat.b, mat.a )
-				End If
-			
-			Case ERotationOrder.ZYX
-				y = ASin( -Max( -1, Min( 1, mat.c ) ) )
-		
-				If Abs(mat.c) < 0.9999999 Then
-					x = ATan2( mat.g, mat.k )
-					z = ATan2( mat.b, mat.a )
-				Else
-					z = ATan2( -mat.e, mat.f )
-				End If
-			
-		End Select
-		
-		Return New SVec3F(x, y, z)
-	End Method
-
-	Rem
-	bbdoc: Returns a #String representation of the quaternion.
-	End Rem
-	Method ToString:String() Override
-		Local sb:TStringBuilder = New TStringBuilder
-		
-		sb.Append("(").Append(x).Append(", ").Append(y).Append(", ").Append(z).Append(", ").Append(w).Append(")")
-		
-		Return sb.ToString()
-	End Method
-
-End Struct
-
-Rem
-bbdoc: An #Int backed Quaternion.
-about: Quaternions are used to represent rotations.
-They are compact, don't suffer from gimbal lock and can easily be interpolated.
-End Rem
-Struct SQuatI
-	Field x:Int
-	Field y:Int
-	Field z:Int
-	Field w:Int
-
-	Method New()
-		Self.w = 1
-	End Method
-	
-	Rem
-	bbdoc: Creates a new #SQuatI from the supplied arguments.
-	End Rem
-	Method New(x:Int, y:Int, z:Int, w:Int)
-		Self.x = x
-		Self.y = y
-		Self.z = z
-		Self.w = w
-	End Method
-
-	Rem
-	bbdoc: Creates a new #SQuatI from the rotation specified by the @euler angle and @order.
-	End Rem
-	Function CreateFromEuler:SQuatI(euler:SVec3I, order:ERotationOrder = ERotationOrder.XYZ)
-		Return New SQuatI.EulerRotate(euler, order)
-	End Function
-	
-	Rem
-	bbdoc: Creates a new #SQuatI from the rotation component of matrix @mat.
-	about: see http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
-	End Rem
-	Function CreateFromRotation:SQuatI(mat:SMat4I)
-		Local trace:Double = mat.a + mat.f + mat.k
-		
-		If trace > 0 Then
-		
-			Local s:Double = 0.5 / Sqr( trace + 1 )
-			
-			Return New SQuatI(Int((mat.g - mat.j) * s), Int((mat.i - mat.c) * s), Int((mat.b - mat.e) * s), Int(0.25 / s))
-		
-		Else If mat.a > mat.f And mat.a > mat.k Then
-		
-			Local s:Double = 2.0 * Sqr(1.0 + mat.a - mat.f - mat.k)
-			
-			Return New SQuatI(Int(0.25 * s), Int((mat.e + mat.b) / s), Int((mat.i + mat.c) / s), Int((mat.g - mat.j) / s))
-		
-		Else If mat.f > mat.k Then
-		
-			Local s:Double = 2.0 * Sqr(1.0 + mat.f - mat.a - mat.k)
-		
-			Return New SQuatI(Int((mat.e + mat.b) / s), Int(0.25 * s), Int((mat.j + mat.g) / s), Int((mat.i - mat.c) / s))
-		
-		Else
-		
-			Local s:Double = 2.0 * Sqr( 1.0 + mat.k - mat.a - mat.f )
-		
-			Return New SQuatI(Int((mat.i + mat.c) / s), Int((mat.j + mat.g) / s), Int(0.25 * s), Int((mat.b - mat.e) / s))
-			
-		End If
-		
-	End Function
-
-	Rem
-	bbdoc: Applies the quaternion @a to the matrix, returning a new matrix.
-	End Rem
-	Function ToMat3:SMat3I(a:SQuatI)
-		Local ax:Int = a.x
-		Local ay:Int = a.y
-		Local az:Int = a.z
-		Local aw:Int = a.w
-		Local ax2:Int = ax + ax
-		Local ay2:Int = ay + ay
-		Local az2:Int = az + az
-		Local axx:Int = ax * ax2
-		Local ayx:Int = ay * ax2
-		Local ayy:Int = ay * ay2
-		Local azx:Int = az * ax2
-		Local azy:Int = az * ay2
-		Local azz:Int = az * az2
-		Local awx:Int = aw * ax2
-		Local awy:Int = aw * ay2
-		Local awz:Int = aw * az2
-		Return New SMat3I(1 - ayy - azz, ayx + awz, azx - awy, ayx - awz, Int(1.0 - axx - azz), azy + awx, azx + awy, azy - awx, Int(1.0 - axx - ayy))
-	End Function
-
-	Rem
-	bbdoc: Applies the quaternian to the matrix, return the new matrix.
-	End Rem
-	Function ToMat4:SMat4I(a:SQuatI)
-		Local ax:Int = a.x
-		Local ay:Int = a.y
-		Local az:Int = a.z
-		Local aw:Int = a.w
-		Local ax2:Int = ax + ax
-		Local ay2:Int = ay + ay
-		Local az2:Int = az + az
-		Local axx:Int = ax * ax2
-		Local ayx:Int = ay * ax2
-		Local ayy:Int = ay * ay2
-		Local azx:Int = az * ax2
-		Local azy:Int = az * ay2
-		Local azz:Int = az * az2
-		Local awx:Int = aw * ax2
-		Local awy:Int = aw * ay2
-		Local awz:Int = aw * az2
-		Return New SMat4I(Int(1.0 - ayy - azz), ayx + awz, azx - awy, 0, ..
-			ayx - awz, Int(1.0 - axx - azz), azy + awx, 0, ..
-			azx + awy, azy - awx, Int(1.0 - axx - ayy), 0, ..
-			0, 0, 0, 1)
-	End Function	
-
-	Rem
-	bbdoc: Creates a translation and rotation matrix.
-	about: The returned matrix is such that it places objects at position @s, oriented in rotation @a.
-	End Rem
-	Function RotTrans:SMat4I(a:SQuatI, s:SVec3I)
-		Local ax:Int = a.x
-		Local ay:Int = a.y
-		Local az:Int = a.x
-		Local aw:Int = a.w
-		Local ax2:Int = ax + ax
-		Local ay2:Int = ay + ay
-		Local az2:Int = az + az
-		Local axx:Int = ax * ax2
-		Local axy:Int = ax * ay2
-		Local axz:Int = ax * az2
-		Local ayy:Int = ay * ay2
-		Local ayz:Int = ay * az2
-		Local azz:Int = az * az2
-		Local awx:Int = aw * ax2
-		Local awy:Int = aw * ay2
-		Local awz:Int = aw * az2
-		Return New SMat4I(Int(1.0 - ayy - azz), axy + awz, axz - awy, 0, ..
-			axy - awz, Int(1.0 - axx - azz), ayz + awx, 0, ..
-			axz + awy, ayz - awx, Int(1.0 - axx - ayy), 0, ..
-			s.x, s.y, s.z, 1)
-	End Function
-	
-	Rem
-	bbdoc: Creates a translation, rotation and scaling matrix.
-	about: The returned matrix is such that it places objects at position @origin, oriented in rotation @a and scaled by @s.
-	End Rem
-	Function RotTransOrigin:SMat4I(a:SQuatI, s:SVec3I, origin:SVec3I)
-		Local ax:Int = a.x
-		Local ay:Int = a.y
-		Local az:Int = a.x
-		Local aw:Int = a.w
-		Local ax2:Int = ax + ax
-		Local ay2:Int = ay + ay
-		Local az2:Int = az + az
-		Local axx:Int = ax * ax2
-		Local axy:Int = ax * ay2
-		Local axz:Int = ax * az2
-		Local ayy:Int = ay * ay2
-		Local ayz:Int = ay * az2
-		Local azz:Int = az * az2
-		Local awx:Int = aw * ax2
-		Local awy:Int = aw * ay2
-		Local awz:Int = aw * az2
-		Local ox:Int = origin.x
-		Local oy:Int = origin.y
-		Local oz:Int = origin.z
-		Local o00:Int = 1.0 - ayy - azz
-		Local o01:Int = axy + awz
-		Local o02:Int = axz - awy
-		Local o10:Int = axy - awz
-		Local o11:Int = 1.0 - axx - azz
-		Local o12:Int = ayz + awx
-		Local o20:Int = axz + awy
-		Local o21:Int = ayz - awx
-		Local o22:Int = 1.0 - axx - ayy
-		Return New SMat4I(o00, o01, o02, 0, ..
-			o10, o11, o12, 0, ..
-			o20, o21, o22, 0, ..
-			s.x + ox - (o00 * ox + o10 * oy + o20 * oz), ..
-			s.y + oy - (o01 * ox + o11 * oy + o21 * oz), ..
-			s.z + oz - (o02 * ox + o12 * oy + o22 * oz), 1)
-	End Function
-
-	Rem
-	bbdoc: Returns the angle between ths quaternion and the quaternion @quat.
-	End Rem
-	Method AngleTo:Double(quat:SQuatI)
-		Local d:Double = Max(-1, Min( 1, dot(quat)))
-		Return 114.591559026:Double * _acos(Abs(d))
-	End Method
-
-	Rem
-	bbdoc: The dot product between two rotations.
-	End Rem
-	Method Dot:Int(b:SQuatI)
-		Return x * b.x + y * b.y + z * b.z + w * b.w
-	End Method
-	
-	Rem
-	bbdoc: Returns the Inverse of rotation.
-	End Rem
-	Method Invert:SQuatI()
-		Local dot:Int = x * x + y * y + z * z + w * w
-		Local invdot:Int
-		If dot <> 0 Then
-			invdot = 1 / dot
-		End If
-		Return New SQuatI(-x * invdot, -y * invdot, -z * invdot, w * invdot)
-	End Method
-	
-	Rem
-	bbdoc: Interpolates between the SQuatI and @b by @t and normalizes the result afterwards.
-	End Rem
-	Method Interpolate:SQuatI(b:SQuatI, t:Int)
-		Return New SQuatI(LerpI(x, b.x, t), LerpI(y, b.y, t), LerpI(z, b.z, t), LerpI(w, b.w, t))
-	End Method
-
-	Rem
-	bbdoc: Computes the length of this quaternion, considered as a 4 dimensional vector.
-	End Rem
-	Method Length:Double()
-		Return Sqr(x * x + y * y + z * z + w * w)
-	End Method
-	
-	Rem
-	bbdoc: Computes the length of this quaternion, considered as a 4 dimensional vector.
-	about: Calculating the squared length instead of the length is much faster.
-	Often if you are comparing lengths of two quaternions you can just compare their squared lengths.
-	End Rem
-	Method LengthSquared:Double()
-		Return x * x + y * y + z * z + w * w
-	End Method
-
-	Rem
-	bbdoc: Multiplies the quaternion by @b, returning a new quaternion.
-	End Rem
-	Method Operator*:SQuatI(b:SQuatI)
-		Return New SQuatI(x * b.w + w * b.x + y * b.z - z * b.y, ..
-			y * b.w + w * b.y + z * b.x - x * b.z, ..
-			z * b.w + w * b.z + x * b.y - y * b.x, ..
-			w * b.w - x * b.x - y * b.y - z * b.z)
-	End Method
-	
-	Rem
-	bbdoc: Returns a new quaternion, negated.
-	End Rem
-	Method Operator-:SQuatI()
-		Return New SQuatI(-x, -y, -z, -w)
-	End Method
-	
-	Rem
-	bbdoc: The identity rotation.
-	End Rem
-	Function Identity:SQuatI()
-		Return New SQuatI(0, 0, 0, 1)
-	End Function
-	
-	Rem
-	bbdoc: Converts this quaternion to one with the same orientation but with a magnitude of 1.
-	End Rem
-	Method Normal:SQuatI()
-		Local length:Double = x * x + y * y + z * z + w * w
-		If length = 0 Then
-			Return New SQuatI(0, 0, 0, 1)
-		Else
-			length = 1 / Sqr(length)
-			Return New SQuatI(Int(x * length), Int(y * length), Int(z * length), Int(w * length))
-		End If
-		Return Self
-	End Method
-
-	Rem
-	bbdoc: Rotates this quaternion by a given angular step @s to the specified quaternion @quat.
-	End Rem
-	Method RotateTowards:SQuatI(quat:SQuatI, s:Double)
-		Local angle:Double = AngleTo(quat)
-		
-		If angle = 0 Then
-			Return Self
-		End If
-		
-		Local t:Double = Min(1, s / angle)
-		
-		Return SphericalInterpolate(quat, t)
-	End Method
-	
-	Rem
-	bbdoc: Spherically interpolates between this SQuatI and @b by @t.
-	End Rem
-	Method SphericalInterpolate:SQuatI(b:SQuatI, t:Double)
-		Local bx:Int = b.x
-		Local by:Int = b.y
-		Local bz:Int = b.z
-		Local bw:Int = b.w
-		Local scale0:Double
-		Local scale1:Double
-
-		Local cosom:Int = x * bx + y * by + z * bz + w * bw
-
-		If cosom < 0 Then
-			cosom = -cosom
-			bx = -bx
-			by = -by
-			bz = -bz
-			bw = -bw
-		End If
-		
-		If 1 - cosom > 0.000001 Then
-			Local omega:Double = _acos(cosom)
-			Local sinom:Double = _sin(omega)
-			scale0 = _sin((1.0 - t) * omega) / sinom
-			scale1 = _sin(t * omega) / sinom
-		Else
-			scale0 = 1 - t
-			scale1 = t
-		End If
-		
-		Return New SQuatI(Int(scale0 * x + scale1 * bx), Int(scale0 * y + scale1 * by), Int(scale0 * z + scale1 * bz), Int(scale0 * w + scale1 * bw))
-	End Method
-	
-	Rem
-	bbdoc: Returns a rotation that rotates around @rot.
-	End Rem
-	Method EulerRotate:SQuatI(rot:SVec3I, order:ERotationOrder = ERotationOrder.XYZ)
-		Local cx:Double = Cos(rot.x * .5)
-		Local cy:Double = Cos(rot.y * .5)
-		Local cz:Double = Cos(rot.z * .5)
-		Local sx:Double = Sin(rot.x * .5)
-		Local sy:Double = Sin(rot.y * .5)
-		Local sz:Double = Sin(rot.z * .5)
-		
-		Select order
-			Case ERotationOrder.XYZ
-				Return New SQuatI(Int(sx * cy * cz + cx * sy * sz), ..
-					Int(cx * sy * cz - sx * cy * sz), ..
-					Int(cx * cy * sz + sx * sy * cz), ..
-					Int(cx * cy * cz - sx * sy * sz))
-			Case ERotationOrder.XZY
-				Return New SQuatI(Int(sx * cy * cz - cx * sy * sz), ..
-					Int(cx * sy * cz - sx * cy * sz), ..
-					Int(cx * cy * sz + sx * sy * cz), ..
-					Int(cx * cy * cz + sx * sy * sz))
-			Case ERotationOrder.YXZ
-				Return New SQuatI(Int(sx * cy * cz + cx * sy * sz), ..
-					Int(cx * sy * cz - sx * cy * sz), ..
-					Int(cx * cy * sz - sx * sy * cz), ..
-					Int(cx * cy * cz + sx * sy * sz))
-			Case ERotationOrder.YZX
-				Return New SQuatI(Int(sx * cy * cz + cx * sy * sz), ..
-					Int(cx * sy * cz + sx * cy * sz), ..
-					Int(cx * cy * sz - sx * sy * cz), ..
-					Int(cx * cy * cz - sx * sy * sz))
-			Case ERotationOrder.ZXY
-				Return New SQuatI(Int(sx * cy * cz - cx * sy * sz), ..
-					Int(cx * sy * cz + sx * cy * sz), ..
-					Int(cx * cy * sz + sx * sy * cz), ..
-					Int(cx * cy * cz - sx * sy * sz))
-			Case ERotationOrder.ZYX
-				Return New SQuatI(Int(sx * cy * cz - cx * sy * sz), ..
-					Int(cx * sy * cz + sx * cy * sz), ..
-					Int(cx * cy * sz - sx * sy * cz), ..
-					Int(cx * cy * cz + sx * sy * sz))
-		End Select
-	End Method
-
-	Rem
-	bbdoc: Returns the quaternion converted to Euler angles, using the specified rotation @order.
-	End Rem
-	Method ToEuler:SVec3I(order:ERotationOrder = ERotationOrder.XYZ)
-		Local q:SQuatI = Normal()
-		Local mat:SMat4I = ToMat4(q)
-		
-		Local x:Int
-		Local y:Int
-		Local z:Int
-
-		Select order
-			Case ERotationOrder.XYZ
-				y = ASin( Max( -1, Min( 1, mat.i ) ) )
-		
-				If Abs(mat.i) < 0.9999999 Then
-					x = ATan2( -mat.j, mat.k )
-					z = ATan2( -mat.e, mat.a )
-				Else
-					x = ATan2( mat.g, mat.f )
-				End If
-				
-			Case ERotationOrder.XZY
-				z = ASin( -Max( -1, Min( 1, mat.e ) ) )
-
-				If Abs(mat.e) < 0.9999999 Then
-					x = ATan2( mat.g, mat.f )
-					y = ATan2( mat.i, mat.a )
-				Else
-					x = ATan2( -mat.j, mat.k )
-				End If
-
-			Case ERotationOrder.YXZ
-				x = ASin( -Max( -1, Min( 1, mat.j ) ) )
-		
-				If Abs(mat.j) < 0.9999999 Then
-					y = ATan2( mat.i, mat.k )
-					z = ATan2( mat.b, mat.f )
-				Else
-					y = ATan2( -mat.c, mat.a )
-				End If
-			
-			Case ERotationOrder.YZX
-				z = ASin( Max( -1, Min( 1, mat.b ) ) )
-		
-				If Abs(mat.b) < 0.9999999 Then
-					x = ATan2( -mat.j, mat.f )
-					y = ATan2( -mat.c, mat.a )
-				Else
-					y = ATan2( mat.i, mat.k )
-				End If
-			
-			Case ERotationOrder.ZXY
-				x = ASin( Max( -1, Min( 1, mat.g ) ) )
-		
-				If Abs(mat.g) < 0.9999999 Then
-					y = ATan2( -mat.c, mat.k )
-					z = ATan2( -mat.e, mat.f )
-				Else
-					z = ATan2( mat.b, mat.a )
-				End If
-			
-			Case ERotationOrder.ZYX
-				y = ASin( -Max( -1, Min( 1, mat.c ) ) )
-		
-				If Abs(mat.c) < 0.9999999 Then
-					x = ATan2( mat.g, mat.k )
-					z = ATan2( mat.b, mat.a )
-				Else
-					z = ATan2( -mat.e, mat.f )
-				End If
-			
-		End Select
-		
-		Return New SVec3I(x, y, z)
-	End Method
-
-	Rem
-	bbdoc: Returns a #String representation of the quaternion.
-	End Rem
-	Method ToString:String() Override
-		Local sb:TStringBuilder = New TStringBuilder
-		
-		sb.Append("(").Append(x).Append(", ").Append(y).Append(", ").Append(z).Append(", ").Append(w).Append(")")
-		
-		Return sb.ToString()
-	End Method
-	
-End Struct
-
-Rem
-bbdoc: The order in which to apply rotations.
-End Rem
-Enum ERotationOrder
-	XYZ
-	XZY
-	YXZ
-	YZX
-	ZXY
-	ZYX
-End Enum
-
-
-Private
-Function Lerp:Double(a:Double, b:Double, t:Double)
-	Return a + (b - a) * t
-End Function
-
-Function LerpF:Float(a:Float, b:Float, t:Float)
-	Return a + (b - a) * t
-End Function
-
-Function LerpI:Int(a:Int, b:Int, t:Int)
-	Return a + (b - a) * t
-End Function
-
-Extern
-	Function _acos:Double(x:Double)="double acos(double)!"
-	Function _sin:Double(x:Double)="double sin(double)!"
-End Extern
-Public

+ 0 - 248
quaternion.mod/tests/test.bmx

@@ -1,248 +0,0 @@
-SuperStrict
-
-Framework brl.standardio
-Import brl.quaternion
-Import BRL.MaxUnit
-
-New TTestSuite.run()
-
-Type SQuatDTest Extends TTest
-
-	Const x:Double = 2
-	Const y:Double = 3
-	Const z:Double = 4
-	Const w:Double = 5
-
-	Method testNew() { test }
-		Local q:SQuatD = New SQuatD
-		assertEquals(0, q.x)
-		assertEquals(0, q.y)
-		assertEquals(0, q.z)
-		assertEquals(1, q.w)
-
-		q = New SQuatD(x, y, z, w)
-		assertEquals(x, q.x)
-		assertEquals(y, q.y)
-		assertEquals(z, q.z)
-		assertEquals(w, q.w)
-	End Method
-
-	Method testAngleTo() { test }
-	
-		Local q1:SQuatD = New SQuatD()
-		Local q2:SQuatD = SQuatD.CreateFromEuler(New SVec3D(0, 180, 0))
-		Local q3:SQuatD = SQuatD.CreateFromEuler(New SVec3D(0, 360, 0))
-
-		assertEquals(0, q1.AngleTo(q1))
-		assertEquals(180, q1.AngleTo(q2), 0.000001)
-		assertEquals(0, q1.AngleTo(q3))
-	
-	End Method
-	
-	Method testCreateFromEuler() { test }
-	
-		Local angles:SVec3D[] = [New SVec3D(45, 0, 0), New SVec3D(0, 45, 0), New SVec3D(0, 0, 45)]
-	
-		For Local order:ERotationOrder = EachIn ERotationOrder.Values()
-		
-			For Local i:Int = 0 Until angles.length
-			
-				Local quat:SQuatD = SQuatD.CreateFromEuler(angles[i], order)
-				Local euler:SVec3D = quat.ToEuler(order)
-				
-				assertTrue(euler.DistanceTo(angles[i]) < 0.001)
-			
-			Next
-		Next
-	
-	End Method
-	
-	Method testCreateFromRotation() { test }
-	
-		Local q1:SQuatD = New SQuatD
-		Local quat:SQuatD = New SQuatD(-9, -2, 3, -4).Normal()
-		Local mat:SMat4D = SQuatD.ToMat4(quat)
-		
-		Local expected:SQuatD = New SQuatD(0.8581163303210332:Double, 0.19069251784911848:Double, -0.2860387767736777:Double, 0.38138503569823695:Double)
-		
-		Local q2:SQuatD = SQuatD.CreateFromRotation(mat)
-		
-		assertTrue(Abs(q2.x - expected.x) <= 0.0001)
-		assertTrue(Abs(q2.y - expected.y) <= 0.0001)
-		assertTrue(Abs(q2.z - expected.z) <= 0.0001)
-		assertTrue(Abs(q2.w - expected.w) <= 0.0001)
-		
-		quat = New SQuatD(-1, -2, 1, -1).Normal()
-		mat = SQuatD.ToMat4(quat)
-		
-		expected:SQuatD = New SQuatD(0.37796447300922714:Double, 0.7559289460184544:Double, -0.37796447300922714:Double, 0.37796447300922714:Double)
-		
-		q2:SQuatD = SQuatD.CreateFromRotation(mat)
-
-		assertTrue(Abs(q2.x - expected.x) <= 0.0001)
-		assertTrue(Abs(q2.y - expected.y) <= 0.0001)
-		assertTrue(Abs(q2.z - expected.z) <= 0.0001)
-		assertTrue(Abs(q2.w - expected.w) <= 0.0001)
-
-	End Method
-
-	Method testDot() { test }
-		
-		Local q1:SQuatD = New SQuatD
-		Local q2:SQuatD = New SQuatD
-		
-		assertEquals(1, q1.Dot(q2))
-		
-		q1 = New SQuatD(1, 2, 3, 1)
-		q2 = New SQuatD(3, 2, 1, 1)
-		
-		assertEquals(11, q1.Dot(q2))
-
-	End Method
-	
-	Method testNormal() { test }
-
-		Local quat:SQuatD = New SQuatD(x, y, z, w)
-		
-		assertTrue(quat.Length() <> 1)
-		assertTrue(quat.LengthSquared() <> 1)
-
-		quat = quat.Normal()
-
-		assertTrue(quat.Length() = 1)
-		assertTrue(quat.LengthSquared() = 1)
-		
-		quat = New SQuatD(0, 0, 0, 0)
-		
-		assertTrue(quat.Length() = 0)
-		assertTrue(quat.LengthSquared() = 0)
-		
-		quat = quat.Normal()
-
-		assertTrue(quat.Length() = 1)
-		assertTrue(quat.LengthSquared() = 1)
-
-	End Method
-
-End Type
-
-Type SQuatFTest Extends TTest
-
-	Const x:Float = 2
-	Const y:Float = 3
-	Const z:Float = 4
-	Const w:Float = 5
-
-	Method testNew() { test }
-		Local q:SQuatF = New SQuatF
-		assertEquals(0, q.x)
-		assertEquals(0, q.y)
-		assertEquals(0, q.z)
-		assertEquals(1, q.w)
-
-		q = New SQuatF(x, y, z, w)
-		assertEquals(x, q.x)
-		assertEquals(y, q.y)
-		assertEquals(z, q.z)
-		assertEquals(w, q.w)
-	End Method
-
-	Method testAngleTo() { test }
-	
-		Local q1:SQuatF = New SQuatF()
-		Local q2:SQuatF = SQuatF.CreateFromEuler(New SVec3F(0, 180, 0))
-		Local q3:SQuatF = SQuatF.CreateFromEuler(New SVec3F(0, 360, 0))
-
-		assertEquals(0, q1.AngleTo(q1))
-		assertEquals(180, q1.AngleTo(q2), 0.000001)
-		assertEquals(0, q1.AngleTo(q3))
-	
-	End Method
-	
-	Method testCreateFromEuler() { test }
-	
-		Local angles:SVec3F[] = [New SVec3F(45, 0, 0), New SVec3F(0, 45, 0), New SVec3F(0, 0, 45)]
-	
-		For Local order:ERotationOrder = EachIn ERotationOrder.Values()
-		
-			For Local i:Int = 0 Until angles.length
-			
-				Local quat:SQuatF = SQuatF.CreateFromEuler(angles[i], order)
-				Local euler:SVec3F = quat.ToEuler(order)
-				
-				assertTrue(euler.DistanceTo(angles[i]) < 0.001)
-			
-			Next
-		Next
-	
-	End Method
-	
-	Method testCreateFromRotation() { test }
-	
-		Local q1:SQuatF = New SQuatF
-		Local quat:SQuatF = New SQuatF(-9, -2, 3, -4).Normal()
-		Local mat:SMat4F = SQuatF.ToMat4(quat)
-		
-		Local expected:SQuatF = New SQuatF(0.8581163303210332:Float, 0.19069251784911848:Float, -0.2860387767736777:Float, 0.38138503569823695:Float)
-		
-		Local q2:SQuatF = SQuatF.CreateFromRotation(mat)
-		
-		assertTrue(Abs(q2.x - expected.x) <= 0.0001)
-		assertTrue(Abs(q2.y - expected.y) <= 0.0001)
-		assertTrue(Abs(q2.z - expected.z) <= 0.0001)
-		assertTrue(Abs(q2.w - expected.w) <= 0.0001)
-		
-		quat = New SQuatF(-1, -2, 1, -1).Normal()
-		mat = SQuatF.ToMat4(quat)
-		
-		expected:SQuatF = New SQuatF(0.37796447300922714:Float, 0.7559289460184544:Float, -0.37796447300922714:Float, 0.37796447300922714:Float)
-		
-		q2:SQuatF = SQuatF.CreateFromRotation(mat)
-
-		assertTrue(Abs(q2.x - expected.x) <= 0.0001)
-		assertTrue(Abs(q2.y - expected.y) <= 0.0001)
-		assertTrue(Abs(q2.z - expected.z) <= 0.0001)
-		assertTrue(Abs(q2.w - expected.w) <= 0.0001)
-
-	End Method
-
-	Method testDot() { test }
-		
-		Local q1:SQuatF = New SQuatF
-		Local q2:SQuatF = New SQuatF
-		
-		assertEquals(1, q1.Dot(q2))
-		
-		q1 = New SQuatF(1, 2, 3, 1)
-		q2 = New SQuatF(3, 2, 1, 1)
-		
-		assertEquals(11, q1.Dot(q2))
-
-	End Method
-	
-	Method testNormal() { test }
-
-		Local quat:SQuatF = New SQuatF(x, y, z, w)
-
-		assertTrue(quat.Length() <> 1)
-		assertTrue(quat.LengthSquared() <> 1)
-
-		quat = quat.Normal()
-
-		assertEquals(1, quat.Length(), 0.0001)
-		assertEquals(1, quat.LengthSquared(), 0.0001)
-		
-		quat = New SQuatF(0, 0, 0, 0)
-		
-		assertTrue(quat.Length() = 0)
-		assertTrue(quat.LengthSquared() = 0)
-		
-		quat = quat.Normal()
-
-		assertEquals(1, quat.Length(), 0.0001)
-		assertEquals(1, quat.LengthSquared(), 0.0001)
-	
-	End Method
-
-End Type
-

+ 0 - 578
vector.mod/doc/intro.bbdoc

@@ -1,578 +0,0 @@
-BlitzMax provides a set of vector structs for working with 2D, 3D, and 4D coordinates. These
-structs come in three variations for each dimension: #Double ( #SVec2D, #SVec3D, #SVec4D), 
-#Float ( #SVec2F, #SVec3F, #SVec4F), and #Int ( #SVec2I, #SVec3I, #SVec4I).
-The choice of struct depends on the precision and coordinate system you require for your application.
-
-Vectors are used to represent positions, directions, and magnitudes in coordinate systems.
-
-## 2D Vectors
-2D vectors are represented by the #SVec2D, #SVec2F, and #SVec2I structs, each catering to
-different data types: #Double, #Float, and #Int, respectively. These structs consist of two
-components, x and y, which correspond to the horizontal and vertical coordinates in a 2D space.
-They are commonly used in 2D games, graphical applications, and geometric calculations.
-
-### 2D Games
-In 2D games, vectors play an essential role in various aspects of game development,
-providing a powerful and efficient way to handle mathematical operations related to game
-objects such as characters, projectiles, and other interactive elements.
-
-**Positions** : Vectors are used to represent the position of game objects in a 2D space.
-The x and y components of the vector store the object's coordinates, which define its location
-on the screen. By manipulating these coordinates, game objects can be moved, placed, or
-arranged in relation to other objects and the game environment.
-
-In this example, the playerPos and targetPos are 2D vectors ( #SVec2F) representing the positions of
-the player object and the target object. The player object is moved by manipulating the x and y
-components of the playerPos vector based on the arrow keys pressed. We also check if the player
-object has reached the target object by calculating the distance between their positions using the
-#DistanceTo method. If the player reaches the target, the target object is moved to a new random position
-on the screen.
-
-```blitzmax
-SuperStrict
-
-Framework sdl.sdlrendermax2d
-Import brl.random
-
-Graphics 800, 600, 0
-
-Local playerPos:SVec2F = New SVec2F(400, 300)
-Local targetPos:SVec2F = New SVec2F(Rand(50, 750), Rand(50, 550))
-
-While Not KeyHit(KEY_ESCAPE)
-    Cls
-
-	Local x:Float = playerPos.x
-	Local y:Float = playerPos.y
-
-    ' Update player position using arrow keys
-    If KeyDown(KEY_LEFT) Then x :- 5
-    If KeyDown(KEY_RIGHT) Then x :+ 5
-    If KeyDown(KEY_UP) Then y :- 5
-    If KeyDown(KEY_DOWN) Then y :+ 5
-
-    ' Set playerPos with updated coordinates.
-	playerPos = New SVec2F(x, y)
-
-    ' Draw player
-    SetColor 0, 255, 0
-    DrawOval(playerPos.x - 10, playerPos.y - 10, 20, 20)
-
-    ' Draw target
-    SetColor 255, 0, 0
-    DrawOval(targetPos.x - 5, targetPos.y - 5, 10, 10)
-
-    ' Check if player reached the target
-    If playerPos.DistanceTo(targetPos) < 15
-        ' Move target to a new random position
-        targetPos = New SVec2F(Rand(50, 750), Rand(50, 550))
-    EndIf
-
-    Flip
-Wend
-```
-
-**Velocities and Accelerations** : Vectors can represent not only positions but also
-velocities and accelerations. In this context, the x and y components of the vector
-indicate the rate of change of an object's position or velocity, respectively. By adding
-velocity vectors to position vectors, game developers can create smooth and realistic motion
-for characters and other moving objects. Similarly, acceleration vectors can be used to simulate
-the effects of gravity, friction, and other forces on the motion of game objects.
-
-In this example, the `ballPos`, `ballVel`, and ballAcc are 2D vectors ( #SVec2F) representing the ball's position,
-velocity, and acceleration. We update the ball's position and velocity based on the acceleration
-(gravity) and time elapsed (`deltaTime`). When the ball hits the floor, we reverse its vertical
-velocity and apply a bounce coefficient to simulate the loss of energy during the bounce.
-
-```blitzmax
-SuperStrict
-
-Framework SDL.SDLRenderMax2D
-
-Graphics 800, 600, 0
-
-Local ballPos:SVec2F = New SVec2F(400, 100)
-Local ballVel:SVec2F = New SVec2F(0, 0)
-Local ballAcc:SVec2F = New SVec2F(0, 9.81) ' Gravity
-Local ballRadius:Float = 20
-Local floorY:Float = 550
-Local deltaTime:Float = 1.0 / 60.0
-Local bounceCoefficient:Float = 0.8
-
-While Not KeyHit(KEY_ESCAPE)
-    Cls
-
-    ' Update ball velocity and position using acceleration and deltaTime
-    ballVel = ballVel + (ballAcc * deltaTime)
-    ballPos = ballPos + (ballVel * deltaTime)
-
-    ' Bounce the ball off the floor
-    If ballPos.y + ballRadius > floorY And ballVel.y > 0
-		ballVel = New SVec2F(ballVel.x, -ballVel.y * bounceCoefficient)
-		ballPos = New SVec2F(ballPos.x, floorY - ballRadius)
-    EndIf
-
-    ' Draw ball
-    SetColor 0, 255, 0
-    DrawOval(ballPos.x - ballRadius, ballPos.y - ballRadius, ballRadius * 2, ballRadius * 2)
-
-    ' Draw floor
-    SetColor 255, 255, 255
-    DrawLine(0, floorY, 800, floorY)
-
-    Flip
-Wend
-```
-
-**Collision Detection and Resolution** : Vectors are crucial in detecting and resolving collisions
-between game objects. By comparing the positions and sizes of objects, developers can identify when
-two objects are overlapping or in close proximity, triggering a collision event. Vectors can also
-be used to calculate the response of objects upon collision, such as bouncing, sliding, or sticking
-together. This is achieved by reflecting or altering the velocity vectors of the colliding objects
-based on their properties, such as mass, elasticity, and friction.
-
-**Distances and Angles** : Vectors can be used to calculate distances and angles between objects,
-which is useful for various game mechanics, such as determining the line of sight for a character
-or measuring the range of a weapon. By subtracting the position vectors of two objects, developers
-can find the vector connecting them and calculate its length to obtain the distance between them.
-Additionally, the dot product and other vector operations can be used to determine the angle between
-two vectors, which is helpful for calculating relative orientations or aiming projectiles.
-
-In this example, the `characterPos` and `targetPos` are 2D vectors ( #SVec2F) representing the
-character's and target's positions. We calculate the connecting vector by subtracting the
-character's position from the target's position. We then use the `Length()` method to find the
-distance between them and the #ATan2() function to calculate the angle. The distance and angle
-are displayed on the screen.
-
-```blitzmax
-SuperStrict
-
-Framework SDL.SDLRenderMax2D
-Import BRL.Math
-
-Graphics 800, 600, 0
-
-Local characterPos:SVec2F = New SVec2F(100, 300)
-Local targetPos:SVec2F = New SVec2F(700, 400)
-Local distance:Float
-Local angle:Float
-
-While Not KeyHit(KEY_ESCAPE)
-    Cls
-
-    ' Calculate the vector connecting the character and the target
-    Local connectingVector:SVec2F = targetPos - characterPos
-
-    ' Calculate the distance between the character and the target
-    distance = connectingVector.Length()
-
-    ' Calculate the angle between the character and the target
-    angle = ATan2(connectingVector.y, connectingVector.x)
-
-    ' Draw character
-    SetColor 255, 0, 0
-    DrawOval(characterPos.x - 10, characterPos.y - 10, 20, 20)
-
-    ' Draw target
-    SetColor 0, 255, 0
-    DrawOval(targetPos.x - 10, targetPos.y - 10, 20, 20)
-
-    ' Draw aiming line
-    SetColor 255, 255, 255
-    DrawLine(characterPos.x, characterPos.y, targetPos.x, targetPos.y)
-
-    ' Display distance and angle
-    SetColor 255, 255, 255
-    DrawText("Distance: " + distance, 10, 10)
-    DrawText("Angle: " + angle, 10, 30)
-
-    Flip
-Wend
-```
-
-In summary, 2D vectors are indispensable tools in game development, providing an efficient and
-flexible way to handle a wide range of mathematical operations related to game objects and their
-interactions within the game world.
-
-### Graphical Applications
-
-In graphical applications, 2D vectors play a vital role in representing and manipulating various
-elements in a coordinate system. They enable developers to perform operations such as translation,
-rotation, and scaling, which are essential for rendering and adjusting graphics on the screen.
-
-**Points** : 2D vectors are used to represent points in a coordinate system, where the x and y
-components store the horizontal and vertical coordinates, respectively. Points are fundamental
-building blocks in graphical applications, as they define the vertices of lines, polygons, and other shapes.
-
-**Lines and Shapes** : By connecting points using vectors, developers can create lines, curves,
-and shapes in a graphical application. Vectors are used to store the vertices of these shapes
-and calculate properties such as their lengths, areas, and perimeters. Additionally, vector operations
-can be applied to determine intersections, containments, and other spatial relationships between different shapes.
-
-In this example, we define a function `CalculateLineLength` that takes two 2D points ( #SVec2D) as
-input, representing the start and end points of a line segment. The function calculates the
-connecting vector between the two points and returns its length. 
-
-```blitzmax
-SuperStrict
-
-Framework BRL.StandardIO
-Import BRL.Vector
-
-Local startPoint:SVec2D = New SVec2D(100, 300)
-Local endPoint:SVec2D = New SVec2D(700, 400)
-
-Local lineLength:Double = CalculateLineLength(startPoint, endPoint)
-
-Print "Line length: " + lineLength
-
-Function CalculateLineLength:Double(p1:SVec2D, p2:SVec2D)
-	Local connectingVector:SVec2D = p2 - p1
-	Return connectingVector.Length()
-End Function
-```
-
-**Translation** : Translation is the process of moving a shape or a group of shapes from one location
-to another within the coordinate system. By adding a translation vector to the position vectors of
-the shape's vertices, developers can shift the shape by a specified amount in both horizontal and
-vertical directions. This operation is essential for animating objects, panning scenes, or aligning
-graphical elements.
-
-**Rotation** : Rotation is the process of rotating a shape or a group of shapes around a specific
-point in the coordinate system. By applying a rotation matrix, which can be derived from trigonometric
-functions or complex numbers, to the position vectors of the shape's vertices, developers can rotate
-the shape by a given angle. This operation is useful for creating effects such as spinning objects,
-rotating scenes, or adjusting the orientation of graphical elements.
-
-**Scaling** : Scaling is the process of resizing a shape or a group of shapes by a specified factor in
-the coordinate system. By multiplying the position vectors of the shape's vertices by a scaling vector,
-developers can uniformly or non-uniformly scale the shape in horizontal and vertical directions. This
-operation is crucial for zooming in and out, resizing objects, or adapting graphical elements to different
-screen resolutions or aspect ratios.
-
-In conclusion, 2D vectors are essential in graphical applications, providing a robust and efficient way
-to represent and manipulate various elements within a coordinate system. They enable developers to perform
-operations such as translation, rotation, and scaling, which are crucial for rendering and adjusting
-graphics on the screen.
-
-### Geometric Calculations
-
-Geometric calculations play an essential role in numerous fields, including computer graphics, physics
-simulations, and engineering applications. 2D vectors are invaluable tools for solving problems related
-to distances, intersections, and areas of shapes, as well as performing linear algebra operations like
-dot products and matrix multiplications.
-
-**Distances** : Calculating distances between points, lines, and shapes is a common task in geometry.
-Using 2D vectors, developers can determine the distance between two points by finding the length of the
-vector representing the difference between their position vectors. Distances from points to lines or other
-shapes can also be computed using vector operations, such as projections and dot products.
-
-**Intersections** : Detecting intersections between lines, rays, and shapes is essential for various
-applications, including collision detection in games and ray tracing in computer graphics. Using 2D vectors,
-developers can apply algebraic and geometric techniques to determine if and where two elements intersect.
-For example, the intersection point of two lines can be found by solving a system of linear equations formed
-by their parametric equations, which are derived from their direction vectors.
-
-In this example, we define a `TLine` type that stores the start and end points of a line segment.
-The function `CalculateIntersection` takes two TLine objects as input and calculates their intersection
-point, if it exists. If the lines are parallel, the function returns #False.
-
-```blitzmax
-SuperStrict
-
-Framework BRL.StandardIO
-Import BRL.Vector
-
-Local line1:TLine = New TLine(New SVec2D(100, 100), New SVec2D(700, 400))
-Local line2:TLine = New TLine(New SVec2D(400, 100), New SVec2D(200, 600))
-
-Local intersection:SVec2D
-Local result:Int = CalculateIntersection(line1, line2, intersection)
-
-If result Then
-	Print "Intersection point: " + intersection.ToString()
-Else
-	Print "Lines are parallel, no intersection"
-End If
-
-Type TLine
-	Field startPoint:SVec2D
-	Field endPoint:SVec2D
-	
-	Method New(startPoint:SVec2D, endPoint:SVec2D)
-		self.startPoint = startPoint
-		self.endPoint = endPoint
-	End Method
-End Type
-
-Function CalculateIntersection:Int(line1:TLine, line2:TLine, intersection:SVec2D Var)
-	Local v1:SVec2D = line1.endPoint - line1.startPoint
-	Local v2:SVec2D = line2.endPoint - line2.startPoint
-	
-	Local crossProduct:Double = v1.x * v2.y - v1.y * v2.x
-	If Abs(crossProduct) < 0.000001 Then
-		Return False ' Lines are parallel
-	End If
-	
-	Local t:Double = ((line1.startPoint.x - line2.startPoint.x) * v2.y - (line1.startPoint.y - line2.startPoint.y) * v2.x) / crossProduct
-	
-	intersection = line1.startPoint + v1 * t
-	Return True
-End Function
-```
-
-**Areas** : Computing the areas of shapes, such as triangles, polygons, and circles, often involves 2D
-vectors. For instance, the area of a triangle can be calculated using the cross product of two of its edge
-vectors, while the area of a polygon can be determined by summing the signed areas of its constituent
-triangles. Additionally, 2D vectors can be used to calculate the moments of inertia and centroids of shapes,
-which are essential properties in mechanics and engineering.
-
-**Dot Products** : The dot product is a fundamental operation in linear algebra that takes two vectors as
-input and returns a scalar value. In the context of 2D vectors, the dot product can be used to determine
-the angle between two vectors, project one vector onto another, or check if two vectors are perpendicular.
-Dot products are widely used in computer graphics for shading calculations and in physics simulations for
-force computations and collision responses.
-
-**Matrix Multiplications** : Matrix multiplication is another essential linear algebra operation that
-involves 2D vectors when dealing with transformations in a two-dimensional space. By multiplying a
-transformation matrix, such as a translation, rotation, or scaling matrix, with a position vector,
-developers can apply the corresponding transformation to a point, line, or shape in the coordinate
-system. Matrix multiplications are fundamental in computer graphics for rendering and animating scenes
-and in physics simulations for updating the positions and orientations of objects.
-
-
-## 3D Vectors
-3D vectors, represented by the #SVec3D, #SVec3F, and #SVec3I structs, have three components:
-x, y, and z, corresponding to coordinates in 3D space. They are essential in various applications,
-including 3D games, computer graphics, physics simulations, and engineering analyses. While some
-of the uses of 3D vectors overlap with those of 2D vectors, they extend to more complex scenarios
-and additional functionality.
-
-**3D Games and Animation** : In 3D games, vectors are crucial for defining the positions, velocities,
-accelerations, and orientations of game objects, such as characters, vehicles, and cameras.
-They also play a vital role in game mechanics like collision detection, pathfinding, and
-physics-based animations. In character animation, 3D vectors are used in skeleton-based systems
-for defining joint positions and rotations, enabling realistic and expressive movements.
-
-In this example, we define a `TGameObject` type with a position and velocity. The `Update` method is
-used to update the game object's position based on its velocity and a given time step (`dt`).
-It demonstrates moving the game object along a simple path using positions and velocities. The
-game object moves towards each point in the pathPoints array and switches
-to the next target when it gets close enough.
-
-```blitzmax
-SuperStrict
-
-Framework BRL.StandardIO
-Import BRL.Vector
-
-Global pathPoints:SVec3D[] = [New SVec3D(0, 0, 0), New SVec3D(5, 10, 0), New SVec3D(10, 5, 0)]
-Global currentTargetIndex:Int = 0
-
-Local gameObject:TGameObject = New TGameObject(pathPoints[currentTargetIndex], New SVec3D(0, 0, 0))
-Local dt:Double = 0.5
-
-For Local i:Int = 1 To 30
-	Local target:SVec3D = pathPoints[currentTargetIndex]
-	Local direction:SVec3D = (target - gameObject.position).Normal()
-	
-	gameObject.velocity = direction * 1.5 ' Set the speed to 1.5 units per step
-	gameObject.Update(dt)
-	
-	Print "Step: " + i + " | " + gameObject.ToString()
-	
-	If gameObject.position.DistanceTo(target) < 0.1 Then ' If the game object is close to the target, switch to the next target
-		currentTargetIndex = (currentTargetIndex + 1) Mod pathPoints.Length
-	End If
-	
-	Delay(200) ' Add a small delay for readability
-Next
-
-Type TGameObject
-	Field position:SVec3D
-	Field velocity:SVec3D
-	
-	Method New(position:SVec3D, velocity:SVec3D)
-		self.position = position
-		self.velocity = velocity
-	End Method
-	
-	Method Update(dt:Double)
-		self.position = self.position + self.velocity * dt
-	End Method
-	
-	Method ToString:String()
-		Return "Position: " + position.ToString() + " | Velocity: " + velocity.ToString()
-	End Method
-End Type
-```
-
-**Computer Graphics** : 3D vectors are the backbone of computer graphics, representing points,
-lines, and polygons in 3D space. They facilitate transformations, such as translation, rotation,
-and scaling, which are vital for rendering 3D models and scenes. In addition, 3D vectors are used
-in lighting calculations and surface shading, enabling the creation of realistic materials and textures.
-
-**Physics Simulations** : In physics simulations, 3D vectors are used to represent quantities like
-force, torque, and momentum. They enable calculations of physical properties, such as the center of
-mass and moments of inertia for rigid bodies. Moreover, 3D vectors are used in numerical methods like
-the Verlet integration and the Runge-Kutta method for simulating the motion of objects under various
-forces and constraints.
-
-This example demonstrates a simple Verlet integration for simulating the motion of a falling particle
-under gravity. The `TParticle` type contains position, previous position, and acceleration.
-The `Integrate` method updates the particle's position using Verlet integration. We create a particle
-and simulate its motion for 100 iterations, printing its position at each step.
-
-```blitzmax
-SuperStrict
-
-Framework brl.standardio
-Import BRL.Vector
-
-Const deltaTime:Float = 0.01
-Const gravity:Float = -9.8
-Const numIterations:Int = 100
-
-Local p:TParticle = New TParticle(New SVec3F(0, 10, 0))
-
-For Local i:Int = 0 To numIterations - 1
-	p.Integrate()
-	Print "Iteration: " + (i + 1) + " Position: " + p.position.ToString()
-Next
-
-Type TParticle
-	Field position:SVec3F
-	Field previousPosition:SVec3F
-	Field acceleration:SVec3F
-	
-	Method New(pos:SVec3F)
-		position = pos
-		previousPosition = pos
-		acceleration = New SVec3F(0, gravity, 0)
-	End Method
-	
-	Method Integrate()
-		Local temp:SVec3F = position
-		position = position * 2 - previousPosition + acceleration * deltaTime * deltaTime
-		previousPosition = temp
-	End Method
-End Type
-```
-
-**Engineering Analyses** : 3D vectors play a significant role in engineering analyses, including structural,
-fluid dynamics, and thermal analyses. They are used to represent stress and strain tensors in structural
-mechanics, velocity and pressure fields in fluid dynamics, and temperature gradients in thermal analyses.
-Additionally, 3D vectors facilitate the visualization and interpretation of complex engineering data,
-like stress distributions and flow patterns.
-
-**Geospatial Applications** : In geospatial applications, 3D vectors are used to represent positions and
-displacements on Earth's surface and in its atmosphere. They facilitate calculations of distances, angles,
-and areas on the curved surface, as well as transformations between various coordinate systems, such as
-geographic, Cartesian, and spherical coordinates.
-
-**Cross Products** : Unlike 2D vectors, 3D vectors have a unique operation called the cross product.
-The cross product takes two input vectors and returns a new vector that is perpendicular to both input
-vectors and has a magnitude proportional to the sine of the angle between them. Cross products are used
-in numerous applications, including calculating surface normals, generating coordinate systems, and
-determining the torque produced by a force.
-
-3D vectors are essential in various applications, extending the functionality of 2D vectors to more complex
-scenarios and additional operations. 
-
-## 4D Vectors
-4D vectors, represented by the #SVec4D, #SVec4F, and #SVec4I structs, have four components: x, y, z,
-and w. While they can indeed represent points in 4D space, their primary use is for homogeneous
-coordinates in 3D graphics, along with other specialized applications.
-
-**Homogeneous Coordinates** : In 3D graphics, 4D vectors are employed to represent homogeneous coordinates,
-which enable affine transformations, such as translation, rotation, scaling, and perspective projection.
-By adding the w component to a 3D point, 4D vectors allow for these transformations to be represented
-by matrix multiplications, simplifying the overall process. Once the transformations are applied, the
-resulting 4D vector can be converted back to a 3D point by dividing the x, y, and z components by
-the w component.
-
-In this example, we define a `TTransformableObject` type with a 4D vector position to represent
-the object's position in homogeneous coordinates. The `ApplyTransformation` method applies a given
-transformation matrix to the object's position using the `Apply` method of #SMat4D. The `Get3DPosition`
-method converts the 4D vector back to a 3D point by dividing the x, y, and z components by the w component.
-
-We create a TransformableObject and use the #SMat4D struct functions `Translation`,
-`Rotation`, and `Scaling` to create translation, rotation, and scaling matrices. We then apply
-these transformations to the object and print its position after each transformation.
-
-```blitzmax
-SuperStrict
-
-Framework BRL.StandardIO
-Import BRL.Vector
-Import BRL.Matrix
-
-Local obj:TTransformableObject = New TTransformableObject(New SVec3D(2.0, 3.0, 4.0))
-
-Local translationMatrix:SMat4D = SMat4D.Translation(New SVec3D(5.0, -2.0, 3.0))
-Local rotationMatrix:SMat4D = SMat4D.Rotation(New SVec3D(0.0, 1.0, 0.0), 45.0)
-Local scalingMatrix:SMat4D = SMat4D.Scaling(New SVec3D(2.0, 0.5, 1.5))
-
-Print "Original Position: " + obj.ToString()
-
-obj.ApplyTransformation(translationMatrix)
-Print "After Translation: " + obj.ToString()
-
-obj.ApplyTransformation(rotationMatrix)
-Print "After Rotation: " + obj.ToString()
-
-obj.ApplyTransformation(scalingMatrix)
-Print "After Scaling: " + obj.ToString()
-
-
-Type TTransformableObject
-	Field position:SVec4D
-	
-	Method New(position:SVec3D)
-		self.position = New SVec4D(position.x, position.y, position.z, 1.0)
-	End Method
-	
-	Method ApplyTransformation(transformationMatrix:SMat4D)
-		self.position = transformationMatrix.Apply(self.position)
-	End Method
-	
-	Method Get3DPosition:SVec3D()
-		Return New SVec3D(self.position.x / self.position.w, self.position.y / self.position.w, self.position.z / self.position.w)
-	End Method
-	
-	Method ToString:String()
-		Return "Position: " + Get3DPosition().ToString()
-	End Method
-End Type
-```
-
-**Perspective Projection** : Perspective projection is a crucial aspect of 3D rendering, as it creates
-the illusion of depth on a 2D screen. 4D vectors facilitate this process by allowing the perspective
-divide, which is a transformation that scales x, y, and z coordinates by the reciprocal of the w component.
-This division produces the desired depth effect, with objects appearing smaller as they move further away
-from the viewer.
-
-**Quaternions** : 4D vectors can also represent quaternions, which are mathematical constructs used for
-efficient 3D rotation calculations. In this context, the x, y, and z components correspond to the
-imaginary part of the quaternion, and the w component corresponds to the real part. Quaternions are
-particularly useful in computer graphics and robotics for avoiding gimbal lock, interpolating between
-orientations, and maintaining numerical stability.
-
-**Color Representation** : In computer graphics, 4D vectors can be used to represent colors with an
-additional alpha channel for transparency. The x, y, z, and w components correspond to the red, green,
-blue, and alpha values, respectively. This representation allows for color blending and modulation
-operations to be performed using vector arithmetic.
-
-**Splines and Curves** : 4D vectors can be employed to represent control points for splines and curves
-in higher-dimensional spaces. For example, in computer-aided design (CAD) and computer graphics,
-4D vectors can define control points for NURBS (Non-uniform rational B-spline) surfaces or Bézier
-patches, enabling smooth and precise modeling of complex shapes.
-
-**Hyperplanes** : In machine learning and computational geometry, 4D vectors can be used to represent
-hyperplanes in 4D space. These hyperplanes can be employed for tasks like data classification,
-clustering, or nearest-neighbor searches in four-dimensional data sets.
-
-4D vectors have specialized uses, primarily in homogeneous coordinates for 3D graphics, along with
-other applications such as perspective projection, quaternion representation, color representation,
-splines and curves, and hyperplanes. While sharing some similarities with 2D and 3D vectors,
-4D vectors extend their functionality to accommodate unique scenarios and mathematical constructs.

+ 0 - 12
vector.mod/doc/svec2d_angleto.bmx

@@ -1,12 +0,0 @@
-SuperStrict
-
-Framework brl.standardio
-Import brl.vector
-
-Local a:SVec2D = New SVec2D(-5, -5)
-Local b:SVec2D = New SVec2D(5, 5)
-
-
-Local c:Double = a.AngleTo(b)
-
-Print c

+ 0 - 14
vector.mod/doc/svec2d_clamp.bmx

@@ -1,14 +0,0 @@
-SuperStrict
-
-Framework brl.standardio
-Import brl.vector
-
-Local a:SVec2D = New SVec2D(0, 0)
-Local b:SVec2D = New SVec2D(10, 5)
-
-Local v:SVec2D = New SVec2D(11, -2)
-
-
-Local c:SVec2D = v.Clamp(a, b)
-
-Print c.ToString() ' 10, 0

+ 0 - 11
vector.mod/doc/svec2d_dot.bmx

@@ -1,11 +0,0 @@
-SuperStrict
-
-Framework brl.standardio
-Import brl.vector
-
-Local a:SVec2D = New SVec2D(-6, 8)
-Local b:SVec2D = New SVec2D(5, 12)
-
-Local dot:Float = a.Dot(b)
-
-Print dot ' 66

+ 0 - 11
vector.mod/doc/svec2d_interpolate.bmx

@@ -1,11 +0,0 @@
-SuperStrict
-
-Framework brl.standardio
-Import brl.vector
-
-Local a:SVec2D = New SVec2D(-6, 8)
-Local b:SVec2D = New SVec2D(5, 12)
-
-Print a.Interpolate(b, 0).ToString() ' -6, 8
-Print a.Interpolate(b, 1).ToString() ' 5, 12
-Print a.Interpolate(b, 0.5).ToString() ' -0.5, 10

+ 0 - 10
vector.mod/doc/svec2d_length.bmx

@@ -1,10 +0,0 @@
-SuperStrict
-
-Framework brl.standardio
-Import brl.vector
-
-Local a:SVec2D = New SVec2D(3, 4)
-
-Local length:Float = a.Length()
-
-Print length ' 5

+ 0 - 10
vector.mod/doc/svec2d_lengthsquared.bmx

@@ -1,10 +0,0 @@
-SuperStrict
-
-Framework brl.standardio
-Import brl.vector
-
-Local a:SVec2D = New SVec2D(3, 4)
-
-Local length:Double = a.LengthSquared()
-
-Print length ' 25

+ 0 - 12
vector.mod/doc/svec2d_max.bmx

@@ -1,12 +0,0 @@
-SuperStrict
-
-Framework brl.standardio
-Import brl.vector
-
-Local a:SVec2D = New SVec2D(12, 8)
-Local b:SVec2D = New SVec2D(10, 16)
-
-
-Local c:SVec2D = a.Max(b)
-
-Print c.ToString() ' 12, 16

+ 0 - 12
vector.mod/doc/svec2d_min.bmx

@@ -1,12 +0,0 @@
-SuperStrict
-
-Framework brl.standardio
-Import brl.vector
-
-Local a:SVec2D = New SVec2D(12, 8)
-Local b:SVec2D = New SVec2D(10, 16)
-
-
-Local c:SVec2D = a.Min(b)
-
-Print c.ToString() ' 10, 8

+ 0 - 10
vector.mod/doc/svec2d_normal.bmx

@@ -1,10 +0,0 @@
-SuperStrict
-
-Framework brl.standardio
-Import brl.vector
-
-Local a:SVec2D = New SVec2D(10, 0)
-
-Local b:SVec2D = a.Normal()
-
-Print b.ToString() ' 1, 0

+ 0 - 11
vector.mod/doc/svec2d_operator_add.bmx

@@ -1,11 +0,0 @@
-SuperStrict
-
-Framework brl.standardio
-Import brl.vector
-
-Local a:SVec2D = New SVec2D(3, 2)
-Local b:SVec2D = New SVec2D(-2, 1)
-
-Local c:SVec2D = a + b
-
-Print c.ToString() ' 1, 3

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.