├── Runtime
├── iShape
│ ├── Triangulation
│ │ ├── Extension.meta
│ │ ├── Shape
│ │ │ ├── Delaunay
│ │ │ │ ├── CentroidNet.cs.meta
│ │ │ │ ├── IndexBuffer.cs.meta
│ │ │ │ ├── Tessellation.cs.meta
│ │ │ │ ├── Side.cs.meta
│ │ │ │ ├── Delaunay.cs.meta
│ │ │ │ ├── SliceBuffer.cs.meta
│ │ │ │ ├── Triangle.cs.meta
│ │ │ │ ├── ConvexPolygon.cs.meta
│ │ │ │ ├── TriangleStack.cs.meta
│ │ │ │ ├── Triangulation.cs.meta
│ │ │ │ ├── Side.cs
│ │ │ │ ├── IndexBuffer.cs
│ │ │ │ ├── TriangleStack.cs
│ │ │ │ ├── Triangle.cs
│ │ │ │ ├── ConvexPolygon.cs
│ │ │ │ ├── SliceBuffer.cs
│ │ │ │ ├── Triangulation.cs
│ │ │ │ ├── Tessellation.cs
│ │ │ │ ├── CentroidNet.cs
│ │ │ │ └── Delaunay.cs
│ │ │ ├── Delaunay.meta
│ │ │ ├── LinkNature.cs
│ │ │ ├── Slice.cs
│ │ │ ├── Link.cs.meta
│ │ │ ├── Slice.cs.meta
│ │ │ ├── LayoutExt.cs.meta
│ │ │ ├── LinkNature.cs.meta
│ │ │ ├── MonotoneLayout.cs.meta
│ │ │ ├── NavigatorExt.cs.meta
│ │ │ ├── ShapeNavigator.cs.meta
│ │ │ ├── TriangulationExt.cs.meta
│ │ │ ├── Link.cs
│ │ │ ├── MonotoneLayout.cs
│ │ │ ├── ShapeNavigator.cs
│ │ │ ├── TriangulationExt.cs
│ │ │ └── NavigatorExt.cs
│ │ ├── Extension
│ │ │ ├── ConvexPolygonTriangulationExt.cs.meta
│ │ │ └── ConvexPolygonTriangulationExt.cs
│ │ ├── Shape.meta
│ │ ├── Util.meta
│ │ └── Util
│ │ │ ├── IntTriangle.cs.meta
│ │ │ └── IntTriangle.cs
│ └── Triangulation.meta
├── iShape.meta
├── iShape.Triangulation.asmdef.meta
└── iShape.Triangulation.asmdef
├── LICENSE.meta
├── CHANGELOG.md.meta
├── README.md.meta
├── package.json.meta
├── Readme
├── eagle_centroid.svg.meta
├── star_polygon.svg.meta
├── star_triangle.svg.meta
├── cheese_example_0.svg.meta
├── cheese_example_1.svg.meta
├── cheese_example_2.svg.meta
├── cheese_example_3.svg.meta
├── eagle_tessellation.svg.meta
├── eagle_triangles_extra_points.svg.meta
├── star_polygon.svg
└── star_triangle.svg
├── Readme.meta
├── Runtime.meta
├── Tests.meta
├── Tests
├── Editor.meta
└── Editor
│ ├── Triangulation.meta
│ ├── Triangulation
│ ├── Data.meta
│ ├── Util.meta
│ ├── Util
│ │ ├── Triangle.cs.meta
│ │ ├── IntArrayExt.cs.meta
│ │ ├── Triangle.cs
│ │ └── IntArrayExt.cs
│ ├── ComplexPlainTests.cs.meta
│ ├── Data
│ │ ├── ComplexTests.cs.meta
│ │ ├── MonotoneTests.cs.meta
│ │ └── MonotoneTests.cs
│ ├── MonotonePlainTests.cs.meta
│ ├── ComplexDelaunayTests.cs.meta
│ ├── MonotoneDelaunayTests.cs.meta
│ ├── MonotoneDelaunayTests.cs
│ ├── MonotonePlainTests.cs
│ └── ComplexDelaunayTests.cs
│ ├── iShape.Triangulation.Editor.Tests.asmdef.meta
│ └── iShape.Triangulation.Editor.Tests.asmdef
├── CHANGELOG.md
├── .gitignore
├── package.json
├── LICENSE
└── README.md
/Runtime/iShape/Triangulation/Extension.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 19cb119c8bcb46d4acb5c360b44d761e
3 | timeCreated: 1608964616
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/Delaunay/CentroidNet.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 09bbbec3178e4caeaec2c443df6092ff
3 | timeCreated: 1595523342
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/Delaunay/IndexBuffer.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 2f61dd810689484d9c485bb6b6183901
3 | timeCreated: 1609009188
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/Delaunay/Tessellation.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 052a3f26df7b46ceb3e4481e0ada6300
3 | timeCreated: 1595519503
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Extension/ConvexPolygonTriangulationExt.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 34bd7c6244344c47a1eda14fe44991af
3 | timeCreated: 1608964642
--------------------------------------------------------------------------------
/LICENSE.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 7f57ace8ab0124cf68f06bd4e4a33977
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/CHANGELOG.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: c09f5fda5429c4250b2d2eb9611bc4be
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/README.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 499cee9e7a0bc4b34bf552c884b61c44
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/package.json.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: f3aea397d873d4152b741097b2380be2
3 | PackageManifestImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Readme/eagle_centroid.svg.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 0aa94e1542b744f2686b016b9541c1fe
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Readme/star_polygon.svg.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: ba2b985eb55e34abba1a43e93755b0a1
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Readme/star_triangle.svg.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 9f7066f1254ec47fa99c06558fa74bd6
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Readme.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: d337a014a101b478ea9dbb64f17099b8
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Readme/cheese_example_0.svg.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 1fd1482e5c17849eea9279aac7a8566b
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Readme/cheese_example_1.svg.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: b1c72c6e3d70d4635b66079d2d1b07fb
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Readme/cheese_example_2.svg.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 7be4b815790814ca5bc0a94c281fd7f9
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Readme/cheese_example_3.svg.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e12ab8b49d8f846f0a704a098a5b13c8
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Readme/eagle_tessellation.svg.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: ae74e06aaad204d2e935b195efde6877
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Runtime.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 3b461e94009e74849a48af546f88925e
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Tests.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 64ceb1b747c7e4ea8b37d4028d67b283
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Runtime/iShape.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 32a5b52252bfc4bc4820a6702c1ae24a
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Tests/Editor.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: bbb7859e239394c2187ff4e0a93479f9
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Readme/eagle_triangles_extra_points.svg.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: db430bb6c03e54e0086c9daa4b0c40ef
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Runtime/iShape.Triangulation.asmdef.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 9267cde3c47644be2aa278c43d7895c3
3 | AssemblyDefinitionImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 612555ed3a4804a39bc21926b779a8f2
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Tests/Editor/Triangulation.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 2d9074840c9344a92b78642bcd2b0ac7
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 715a1252f942944a5ad65d4203519f04
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Util.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: d199ce3a16da14250bf9c3594b093440
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Tests/Editor/Triangulation/Data.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 770555f36f14540fea6626fc655648e5
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Tests/Editor/Triangulation/Util.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 49d35b4453a3c43e1aef77948f878b76
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/Delaunay.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 7a25c8c7491fa48b891d7c44cdd2db24
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Tests/Editor/iShape.Triangulation.Editor.Tests.asmdef.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: c65af2c98251b4ca790782041db7267d
3 | AssemblyDefinitionImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/LinkNature.cs:
--------------------------------------------------------------------------------
1 | namespace iShape.Triangulation.Shape {
2 |
3 | internal enum LinkNature {
4 | end = 0,
5 | start = 1,
6 | split = 2,
7 | extra = 3,
8 | merge = 4,
9 | simple = 5
10 | }
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/Slice.cs:
--------------------------------------------------------------------------------
1 | namespace iShape.Triangulation.Shape {
2 |
3 | public readonly struct Slice {
4 | public readonly int a;
5 | public readonly int b;
6 |
7 | public Slice(int a, int b) {
8 | this.a = a;
9 | this.b = b;
10 | }
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/Link.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 7304c39b9e8b4494d8e78a9ab8717206
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/Slice.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 1d88be09995ae457c8e5ad5101088141
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Tests/Editor/Triangulation/Util/Triangle.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 2d5c73553a704401caf03b282cf2ccbf
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/LayoutExt.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 1692db967578848a78a9a44f1b310291
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/LinkNature.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: ee4fb87dff53844028e2e9ce7df69eb1
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Util/IntTriangle.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: aa6634086df7e49618555d917d834b90
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Tests/Editor/Triangulation/ComplexPlainTests.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: a6b54215b9ef645aab5ba8f54c02d87a
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Tests/Editor/Triangulation/Data/ComplexTests.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 269529d6350ee41ddaa764e5c4cdeece
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Tests/Editor/Triangulation/Data/MonotoneTests.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: bc23e12d71ead494294273441d3908d8
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Tests/Editor/Triangulation/MonotonePlainTests.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 63eb6efe885a74cd895731ccdead9e9a
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Tests/Editor/Triangulation/Util/IntArrayExt.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: cf7084246a35c4634aaaf126e9371de8
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | ## [0.0.8] - 2024-07-05
4 |
5 | ### Fixes
6 |
7 | - Test assembly
8 |
9 | ## [0.0.7] - 2024-03-07
10 |
11 | ### Fixes
12 |
13 | - Memory leak
14 |
15 | ## [0.0.2] - 2023-03-05
16 |
17 | ### Fixes
18 |
19 | - Fix dependencies
20 |
21 | ## [0.0.1] - 2020-09-15
22 |
23 | ### Added
24 |
25 | - First release
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/Delaunay/Side.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: cbde6754af07a4a0a9fba3c447ed1320
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/MonotoneLayout.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 2d051050221aa455db982a4c5bb85ab7
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/NavigatorExt.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: d21668a42eb5c400eac37bb43003662c
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/ShapeNavigator.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 74fb18fc43c8f429db0617cc717a02ec
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/TriangulationExt.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 993fa31efd59f4a749f8d628364a9b96
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Tests/Editor/Triangulation/ComplexDelaunayTests.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 6fdeb7aa915ab4cfdbb518d30745f02a
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Tests/Editor/Triangulation/MonotoneDelaunayTests.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 4af37712590e74d6c900fdbbb815c5f0
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/Delaunay/Delaunay.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 4f492862e32124869a56d29d908e1685
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/Delaunay/SliceBuffer.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: aa9584033990c4c0cae97dbff81629cf
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/Delaunay/Triangle.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 2eab2015208e040d99bf0551640b5d65
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/Delaunay/ConvexPolygon.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: a1664064a5bb345c9810e56c01794a38
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/Delaunay/TriangleStack.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 7c92cff999dd240c1bcdbdc0f866b101
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/Delaunay/Triangulation.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 529a109d3fa1a4d4aa0b1c2e37e364c5
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/Delaunay/Side.cs:
--------------------------------------------------------------------------------
1 | namespace iShape.Triangulation.Shape.Delaunay {
2 |
3 | internal struct Side {
4 |
5 | internal readonly int id;
6 | internal int edge;
7 | internal int triangle;
8 |
9 | internal bool IsEmpty => triangle == -1;
10 |
11 | internal Side(int id, int edge, int triangle) {
12 | this.id = id;
13 | this.edge = edge;
14 | this.triangle = triangle;
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/Link.cs:
--------------------------------------------------------------------------------
1 | using iShape.Geometry;
2 |
3 | namespace iShape.Triangulation.Shape {
4 |
5 | public struct Link {
6 | public static readonly Link empty = new Link(0, 0, 0, Vertex.empty);
7 |
8 | public int prev;
9 | public readonly int self;
10 | public int next;
11 |
12 | public Vertex vertex;
13 |
14 | public Link(int prev, int self, int next, Vertex vertex) {
15 | this.prev = prev;
16 | this.self = self;
17 | this.next = next;
18 | this.vertex = vertex;
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | [Ll]ibrary/
2 | [Tt]emp/
3 | [Oo]bj/
4 | [Bb]uild/
5 | [Bb]uilds/
6 | Assets/AssetStoreTools*
7 |
8 | # Visual Studio cache directory
9 | .vs/
10 |
11 | # Autogenerated VS/MD/Consulo solution and project files
12 | ExportedObj/
13 | .consulo/
14 | *.csproj
15 | *.unityproj
16 | *.sln
17 | *.suo
18 | *.tmp
19 | *.user
20 | *.userprefs
21 | *.pidb
22 | *.booproj
23 | *.svd
24 | *.pdb
25 | *.opendb
26 |
27 | # Unity3D generated meta files
28 | *.pidb.meta
29 | *.pdb.meta
30 |
31 | # Unity3D Generated File On Crash Reports
32 | sysinfo.txt
33 |
34 | # Builds
35 | *.apk
36 | *.unitypackage
37 |
--------------------------------------------------------------------------------
/Tests/Editor/iShape.Triangulation.Editor.Tests.asmdef:
--------------------------------------------------------------------------------
1 | {
2 | "name": "iShape.Triangulation.Editor.Tests",
3 | "rootNamespace": "Tests.Triangulation",
4 | "references": [
5 | "iShape.Geometry",
6 | "iShape.Triangulation"
7 | ],
8 | "includePlatforms": [
9 | "Editor"
10 | ],
11 | "excludePlatforms": [],
12 | "allowUnsafeCode": true,
13 | "overrideReferences": false,
14 | "precompiledReferences": [
15 | "nunit.framework.dll"
16 | ],
17 | "autoReferenced": false,
18 | "defineConstraints": [
19 | "UNITY_INCLUDE_TESTS"
20 | ],
21 | "versionDefines": [],
22 | "noEngineReferences": false
23 | }
24 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "com.ishape.triangulation",
3 | "version": "0.0.8",
4 | "displayName": "iShape.Triangulation",
5 | "description": "Complex polygon triangulation. A fast O(n*log(n)) algorithm based on 'Triangulation of monotone polygons'. The result can be represented as a Delaunay triangulation.",
6 | "unity": "2022.2",
7 | "keywords": [
8 | "iShape",
9 | "triangulation",
10 | "tesselation",
11 | "centroid"
12 | ],
13 | "license": "MIT",
14 | "author": {
15 | "name": "iShape",
16 | "email": "nailxsharipov@gmail.com"
17 | },
18 | "repository": {
19 | "type": "git",
20 | "url": "https://github.com/iShapeUnity/Triangulation.git"
21 | },
22 | "dependencies": {
23 | "com.ishape.geometry": "0.0.5",
24 | "com.unity.mathematics": "1.3.1",
25 | "com.unity.collections": "2.2.0"
26 | }
27 | }
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/MonotoneLayout.cs:
--------------------------------------------------------------------------------
1 | using Unity.Collections;
2 |
3 | namespace iShape.Triangulation.Shape {
4 |
5 | public struct MonotoneLayout {
6 |
7 | public readonly int pathCount;
8 | public readonly int extraCount;
9 | public NativeArray links;
10 | public NativeArray slices;
11 | public NativeArray indices;
12 |
13 | public MonotoneLayout(int pathCount, int extraCount, NativeArray links, NativeArray slices, NativeArray indices) {
14 | this.pathCount = pathCount;
15 | this.extraCount = extraCount;
16 | this.links = links;
17 | this.slices = slices;
18 | this.indices = indices;
19 | }
20 |
21 | public void Dispose() {
22 | links.Dispose();
23 | slices.Dispose();
24 | indices.Dispose();
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/Runtime/iShape.Triangulation.asmdef:
--------------------------------------------------------------------------------
1 | {
2 | "name": "iShape.Triangulation",
3 | "rootNamespace": "",
4 | "references": [
5 | "iShape.Geometry",
6 | "Unity.Collections",
7 | "Unity.Mathematics"
8 | ],
9 | "includePlatforms": [],
10 | "excludePlatforms": [],
11 | "allowUnsafeCode": false,
12 | "overrideReferences": false,
13 | "precompiledReferences": [],
14 | "autoReferenced": true,
15 | "defineConstraints": [],
16 | "versionDefines": [
17 | {
18 | "name": "com.ishape.geometry",
19 | "expression": "0.0.3"
20 | },
21 | {
22 | "name": "com.unity.mathematics",
23 | "expression": "1.2.6"
24 | },
25 | {
26 | "name": "com.unity.collections",
27 | "expression": "2.1.4"
28 | }
29 | ],
30 | "noEngineReferences": false
31 | }
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/ShapeNavigator.cs:
--------------------------------------------------------------------------------
1 | using Unity.Collections;
2 |
3 | namespace iShape.Triangulation.Shape {
4 |
5 | internal struct ShapeNavigator {
6 |
7 | internal readonly int pathCount;
8 | internal readonly int extraCount;
9 | internal NativeArray links;
10 | internal NativeArray natures;
11 | internal NativeArray indices;
12 |
13 | internal ShapeNavigator(int pathCount, int extraCount, NativeArray links, NativeArray natures, NativeArray indices) {
14 | this.pathCount = pathCount;
15 | this.extraCount = extraCount;
16 | this.links = links;
17 | this.natures = natures;
18 | this.indices = indices;
19 | }
20 |
21 | internal void Dispose() {
22 | this.links.Dispose();
23 | this.natures.Dispose();
24 | this.indices.Dispose();
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/Tests/Editor/Triangulation/Util/Triangle.cs:
--------------------------------------------------------------------------------
1 | using iShape.Geometry;
2 | using Unity.Collections;
3 |
4 | namespace Tests.Triangulation.Util {
5 |
6 | internal struct Triangle {
7 |
8 | internal static bool IsCCW(NativeArray points, NativeArray triangles) {
9 | int n = triangles.Length;
10 |
11 | var i = 0;
12 | while(i < n) {
13 | int ai = triangles[i];
14 | int bi = triangles[i + 1];
15 | int ci = triangles[i + 2];
16 |
17 |
18 | var a = points[ai];
19 |
20 | var b = points[bi];
21 |
22 | var c = points[ci];
23 |
24 | if(!IsCCW_or_Line(a, b, c)) {
25 | return false;
26 |
27 | }
28 | i += 3;
29 | }
30 | return true;
31 | }
32 |
33 | private static bool IsCCW_or_Line(IntVector a, IntVector b, IntVector c) {
34 | long m0 = (c.y - a.y) * (b.x - a.x);
35 |
36 | long m1 = (b.y - a.y) * (c.x - a.x);
37 |
38 | return m0 <= m1;
39 | }
40 | }
41 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 iShape
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Extension/ConvexPolygonTriangulationExt.cs:
--------------------------------------------------------------------------------
1 | using iShape.Geometry.Polygon;
2 | using Unity.Collections;
3 | using UnityEngine;
4 |
5 | namespace iShape.Triangulation.Extension {
6 |
7 | public static class ConvexPolygonTriangulationExt {
8 |
9 | public static NativeArray GetTriangles(this Polygon self, Allocator allocator) {
10 | int n = self.points.Length;
11 | var count = 3 * (n - 2);
12 | var triangles = new NativeArray(count, allocator);
13 | for (int i = 2, j = 0; i < n; ++i, j += 3) {
14 | triangles[j] = 0;
15 | triangles[j + 1] = i - 1;
16 | triangles[j + 2] = i;
17 | }
18 |
19 | return triangles;
20 | }
21 |
22 | public static NativeArray GetVertices(this Polygon self, Allocator allocator) {
23 | int n = self.points.Length;
24 | var vertices = new NativeArray(n, allocator);
25 | for (int i = 0; i < n; ++i) {
26 | vertices[i] = self.points[i];
27 | }
28 | return vertices;
29 | }
30 | }
31 |
32 | }
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Util/IntTriangle.cs:
--------------------------------------------------------------------------------
1 | using iShape.Geometry;
2 |
3 | namespace iShape.Triangulation.Util {
4 |
5 | public struct IntTriangle {
6 |
7 | public enum Orientation {
8 | clockWise, counterClockWise, line
9 | }
10 |
11 | public static Orientation GetOrientation(IntVector a, IntVector b, IntVector c) {
12 | long m0 = (c.y - a.y) * (b.x - a.x);
13 | long m1 = (b.y - a.y) * (c.x - a.x);
14 |
15 | if(m0 < m1) {
16 | return Orientation.clockWise;
17 | } else if(m0 > m1) {
18 | return Orientation.counterClockWise;
19 | } else {
20 | return Orientation.line;
21 | }
22 | }
23 |
24 | public static bool IsNotLine(IntVector a, IntVector b, IntVector c) {
25 | long m0 = (c.y - a.y) * (b.x - a.x);
26 | long m1 = (b.y - a.y) * (c.x - a.x);
27 |
28 | return m0 != m1;
29 | }
30 |
31 | public static bool IsCCW_or_Line(IntVector a, IntVector b, IntVector c) {
32 | long m0 = (c.y - a.y) * (b.x - a.x);
33 | long m1 = (b.y - a.y) * (c.x - a.x);
34 |
35 | return m0 <= m1;
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/Tests/Editor/Triangulation/Util/IntArrayExt.cs:
--------------------------------------------------------------------------------
1 | using Unity.Collections;
2 | using System.Collections.Generic;
3 |
4 | namespace Tests.Triangulation.Util {
5 |
6 | public static class IntArrayExt {
7 |
8 | private struct Triangle {
9 |
10 | private readonly int a;
11 | private readonly int b;
12 | private readonly int c;
13 |
14 | internal Triangle(int a, int b, int c) {
15 | this.a = a;
16 | this.b = b;
17 | this.c = c;
18 | }
19 |
20 | public override bool Equals(object obj) {
21 | return (obj is Triangle triangle) && Equals(triangle);
22 | }
23 |
24 | private bool Equals(Triangle other) {
25 | return
26 | this.a == other.a && this.b == other.b && this.c == other.c ||
27 | this.a == other.b && this.b == other.c && this.c == other.a ||
28 | this.a == other.c && this.b == other.a && this.c == other.b;
29 | }
30 |
31 | public override int GetHashCode() {
32 | if(a > b && a > c) {
33 | return a;
34 | }
35 |
36 | if(b > a && b > c) {
37 | return b;
38 | }
39 |
40 | return c;
41 | }
42 | }
43 |
44 | public static bool CompareTriangles(this NativeArray self, NativeArray array) {
45 | int n = self.Length;
46 | if(n != array.Length || n % 3 != 0) {
47 | return false;
48 | }
49 |
50 | var set = new HashSet();
51 | for(int i = 0; i < n; i += 3) {
52 | set.Add(new Triangle(self[i], self[i + 1], self[i + 2]));
53 | }
54 |
55 | for(int i = 0; i < n; i += 3) {
56 | var trinagle = new Triangle(array[i], array[i + 1], array[i + 2]);
57 | if (!set.Remove(trinagle)) {
58 | return false;
59 | }
60 | }
61 |
62 | return true;
63 | }
64 |
65 |
66 | }
67 | }
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/Delaunay/IndexBuffer.cs:
--------------------------------------------------------------------------------
1 | using iShape.Collections;
2 | using Unity.Collections;
3 |
4 | namespace iShape.Triangulation.Shape.Delaunay {
5 | internal struct IndexBuffer {
6 |
7 | private struct Link {
8 | internal static readonly Link empty = new Link(true, -1);
9 | internal readonly bool isFree;
10 | internal int next;
11 |
12 | internal Link(bool isFree, int next) {
13 | this.isFree = isFree;
14 | this.next = next;
15 | }
16 | }
17 |
18 | private DynamicArray array;
19 | private int first;
20 | internal IndexBuffer(int count, Allocator allocator) {
21 | this.array = new DynamicArray(count, count, allocator);
22 | if (count == 0) {
23 | this.first = -1;
24 | }
25 | this.first = 0;
26 | for (int i = 0; i < count - 1; ++i) {
27 | this.array[i] = new Link(false, i + 1);
28 | }
29 |
30 | this.array[count - 1] = new Link(false, -1);
31 | }
32 |
33 | internal bool hasNext => first >= 0;
34 |
35 | internal int Next() {
36 | int index = first;
37 | first = array[index].next;
38 | array[index] = Link.empty;
39 | return index;
40 | }
41 |
42 | internal void Add(int index) {
43 | var isOverflow = index >= array.Count;
44 | if (isOverflow || array[index].isFree) {
45 | if (isOverflow) {
46 | int count = index - array.Count + 1;
47 | array.Add(Link.empty, count);
48 | }
49 |
50 | array[index] = new Link(false, first);
51 | if (first >= 0) {
52 | var link = array[first];
53 | array[first] = link;
54 | }
55 |
56 | first = index;
57 | }
58 | }
59 |
60 | internal void Dispose() {
61 | this.array.Dispose();
62 | }
63 | }
64 |
65 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Triangulation [Port](https://github.com/iShape-Swift/iShapeTriangulation/)
2 | Complex polygon triangulation, tessellation and split into convex polygons. A fast O(n*log(n)) algorithm based on "Triangulation of monotone polygons". The result can be represented as a Delaunay triangulation.
3 | ## Delaunay triangulation
4 |
5 |
6 |
7 |
8 | ## Triangulation with extra points
9 |
10 |
11 |
12 |
13 | ## Tessellation
14 |
15 |
16 |
17 |
18 | ## Centroid net
19 |
20 |
21 |
22 |
23 | ## Features
24 |
25 | 💡 Fast O(n*log(n)) algorithm based on "Triangulation of monotone polygons"
26 |
27 | 💡 All code is written to suit "Data Oriented Design". No reference type like class, just structs.
28 |
29 | 💡 Supports polygons with holes
30 |
31 | 💡 Supports plain and Delaunay triangulation
32 |
33 | 💡 Supports tesselation
34 |
35 | 💡 Supports building centroid net
36 |
37 | 💡 Same points is not restricted
38 |
39 | 💡 Polygon must not have self intersections
40 |
41 | 💡 Use integer geometry for calculations
42 |
43 | 💡 More then 100 tests
44 |
45 | ## Installation
46 |
47 | To use iShape.Triangulation in your Unity project, follow these steps:
48 |
49 | - Open your Unity project.
50 | - In the top menu, select "Window" > "Package Manager".
51 | - Click on the "+" button in the top-left corner of the Package Manager window.
52 | - Select "Add package from git URL...".
53 | - Enter the following URL: https://github.com/iShapeUnity/Geometry.git
54 | - Click the "Add" button.
55 | - Wait for the package to be imported.
56 | - After repeat all for https://github.com/iShapeUnity/Triangulation.git
57 |
58 | ## Demo
59 |
60 | The [TriangulationDebug](https://github.com/iShapeUnity/TriangulationDebug) project is a samll demo project with some showcases.
61 |
62 |
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/Delaunay/TriangleStack.cs:
--------------------------------------------------------------------------------
1 | using Unity.Collections;
2 | using iShape.Geometry;
3 | using iShape.Collections;
4 |
5 | namespace iShape.Triangulation.Shape.Delaunay {
6 |
7 | internal struct TriangleStack {
8 |
9 | private readonly struct Edge {
10 | internal readonly int a; // vertex index
11 | internal readonly int b; // vertex index
12 | internal readonly int neighbor; // prev triangle index
13 |
14 | internal Edge(int a, int b, int neighbor) {
15 | this.a = a;
16 | this.b = b;
17 | this.neighbor = neighbor;
18 | }
19 | }
20 |
21 | private DynamicArray edges;
22 | private NativeArray triangles;
23 | private readonly Allocator allocator;
24 | private int counter;
25 |
26 | internal TriangleStack(int count, Allocator allocator) {
27 | this.counter = 0;
28 | this.allocator = allocator;
29 | this.edges = new DynamicArray(8, allocator);
30 | this.triangles = new NativeArray(count, allocator);
31 | }
32 |
33 | internal NativeArray Convert() {
34 | edges.Dispose();
35 | if (this.counter == triangles.Length) {
36 | return triangles;
37 | } else {
38 | var newTriangles = new NativeArray(counter, allocator);
39 | newTriangles.Slice(0, counter).CopyFrom(triangles.Slice(0, counter));
40 | triangles.Dispose();
41 | return newTriangles;
42 | }
43 | }
44 |
45 | internal void Reset() {
46 | edges.RemoveAll();
47 | }
48 |
49 | internal void Add(Vertex a, Vertex b, Vertex c) {
50 | if (a.index == b.index || a.index == c.index|| b.index == c.index) {
51 | // ignore triangle with tween vertices
52 | return;
53 | }
54 |
55 | var triangle = new Triangle(this.counter++, a, b, c);
56 |
57 | var ac = this.Pop(a.index, c.index);
58 | if (ac.a != -1) {
59 | var neighbor = triangles[ac.neighbor];
60 |
61 | neighbor.nA = triangle.index;
62 | triangle.nB = neighbor.index;
63 |
64 | triangles[neighbor.index] = neighbor;
65 | }
66 |
67 | var ab = this.Pop(a.index, b.index);
68 | if(ab.a != -1) {
69 | var neighbor = triangles[ab.neighbor];
70 |
71 | neighbor.nA = triangle.index;
72 | triangle.nC = neighbor.index;
73 |
74 | triangles[neighbor.index] = neighbor;
75 | }
76 |
77 | this.edges.Add(new Edge(b.index, c.index, triangle.index)); // bc is always slice
78 |
79 | triangles[triangle.index] = triangle;
80 | }
81 |
82 | private Edge Pop(int a, int b) {
83 | int last = edges.Count - 1;
84 |
85 | var i = 0;
86 | while (i <= last) {
87 | var e = edges[i];
88 | if ((e.a == a || e.a == b) && (e.b == a || e.b == b)) {
89 | if (i != last) {
90 | edges[i] = edges[last];
91 | }
92 | edges.RemoveLast();
93 |
94 | return e;
95 | }
96 | ++i;
97 | }
98 | return new Edge(-1, -1, -1);
99 | }
100 |
101 |
102 | }
103 | }
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/Delaunay/Triangle.cs:
--------------------------------------------------------------------------------
1 | using iShape.Geometry;
2 |
3 | namespace iShape.Triangulation.Shape.Delaunay {
4 |
5 | public struct Triangle {
6 |
7 | internal readonly int index;
8 |
9 | // a(0), b(1), c(2)
10 | internal Vertex vA;
11 | internal Vertex vB;
12 | internal Vertex vC;
13 |
14 | // BC - a(0), AC - b(1), AB - c(2)
15 | internal int nA;
16 | internal int nB;
17 | internal int nC;
18 |
19 | internal Triangle(int index, Vertex a, Vertex b, Vertex c) {
20 | this.index = index;
21 | this.vA = a;
22 | this.vB = b;
23 | this.vC = c;
24 |
25 | this.nA = -1;
26 | this.nB = -1;
27 | this.nC = -1;
28 | }
29 |
30 | internal Triangle(int index, Vertex a, Vertex b, Vertex c, int nA, int nB, int nC) {
31 | this.index = index;
32 | this.vA = a;
33 | this.vB = b;
34 | this.vC = c;
35 |
36 | this.nA = nA;
37 | this.nB = nB;
38 | this.nC = nC;
39 | }
40 |
41 | internal Vertex Vertex(int i) {
42 | switch(i) {
43 | case 0:
44 | return vA;
45 | case 1:
46 | return vB;
47 | default:
48 | return vC;
49 | }
50 | }
51 |
52 | internal int FindIndex(int vertexIndex) {
53 | if(vA.index == vertexIndex) {
54 | return 0;
55 | }
56 |
57 | return vB.index == vertexIndex ? 1 : 2;
58 | }
59 |
60 | internal int Opposite(int neighbor) {
61 | if(nA == neighbor) {
62 | return 0;
63 | }
64 |
65 | return nB == neighbor ? 1 : 2;
66 | }
67 |
68 | internal Vertex OppositeVertex(int neighbor) {
69 | if(nA == neighbor) {
70 | return vA;
71 | }
72 |
73 | return nB == neighbor ? vB : vC;
74 | }
75 |
76 | internal int Neighbor(int i) {
77 | switch(i) {
78 | case 0:
79 | return nA;
80 | case 1:
81 | return nB;
82 | default:
83 | return nC;
84 | }
85 | }
86 |
87 | internal int FindNeighbor(int vertexIndex) {
88 | if(vA.index == vertexIndex) {
89 | return nA;
90 | }
91 |
92 | return vB.index == vertexIndex ? nB : nC;
93 | }
94 |
95 | internal void SetNeighbor(int i, int value) {
96 | switch(i) {
97 | case 0:
98 | nA = value;
99 | break;
100 | case 1:
101 | nB = value;
102 | break;
103 | default:
104 | nC = value;
105 | break;
106 | }
107 | }
108 |
109 | internal void SetVertex(int i, Vertex value) {
110 | switch(i) {
111 | case 0:
112 | vA = value;
113 | break;
114 | case 1:
115 | vB = value;
116 | break;
117 | default:
118 | vC = value;
119 | break;
120 | }
121 | }
122 |
123 | internal void Update(Vertex vertex) {
124 | if (vA.index != vertex.index) {
125 | vA = vertex;
126 | } else if (vB.index != vertex.index) {
127 | vB = vertex;
128 | } else if (vC.index != vertex.index) {
129 | vC = vertex;
130 | }
131 | }
132 |
133 | internal void UpdateOpposite(int oldNeighbor, int newNeighbor) {
134 | if(nA == oldNeighbor) {
135 | nA = newNeighbor;
136 | return;
137 | }
138 |
139 | if(nB == oldNeighbor) {
140 | nB = newNeighbor;
141 | return;
142 | }
143 |
144 | if(nC == oldNeighbor) {
145 | nC = newNeighbor;
146 | }
147 | }
148 |
149 | internal int AdjacentNeighbor(int vertex, int neighbor) {
150 | if (vA.index != vertex && nA != neighbor) {
151 | return nA;
152 | }
153 | if (vB.index != vertex && nB != neighbor) {
154 | return nB;
155 | }
156 | return nC;
157 | }
158 | }
159 |
160 | }
161 |
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/Delaunay/ConvexPolygon.cs:
--------------------------------------------------------------------------------
1 | using iShape.Collections;
2 | using iShape.Geometry;
3 | using iShape.Geometry.Container;
4 | using iShape.Geometry.Polygon;
5 | using Unity.Collections;
6 |
7 | namespace iShape.Triangulation.Shape.Delaunay {
8 |
9 | internal struct ConvexPolygon {
10 | internal readonly struct Edge {
11 | internal readonly int triangleIndex;
12 | internal readonly int neighbor;
13 | internal readonly int a;
14 | internal readonly int b;
15 |
16 | internal Edge(int triangleIndex, int neighbor, int a, int b) {
17 | this.triangleIndex = triangleIndex;
18 | this.neighbor = neighbor;
19 | this.a = a;
20 | this.b = b;
21 | }
22 | }
23 |
24 | internal DynamicArray edges;
25 | private DynamicArray links;
26 |
27 | internal NativeArray Points(Allocator allocator) {
28 | int n = this.links.Count;
29 | var result = new NativeArray(n, allocator);
30 | for (int i = 0; i < n; ++i) {
31 | result[i] = this.links[i].vertex.point;
32 | }
33 |
34 | return result;
35 | }
36 |
37 | internal bool Add(Edge edge, Triangle triangle) {
38 | var v = triangle.OppositeVertex(edge.triangleIndex);
39 |
40 | // a0 -> a1 -> p
41 | var link_a1 = this.links[edge.a];
42 | var va0 = this.links[link_a1.prev].vertex;
43 | var va1 = link_a1.vertex;
44 |
45 | var aa = va1.point - va0.point;
46 | var ap = v.point - va1.point;
47 |
48 | var apa = aa.CrossProduct(ap);
49 | if (apa > 0) {
50 | return false;
51 | }
52 |
53 | // b0 <- b1 <- p
54 |
55 | var link_b1 = this.links[edge.b];
56 | var vb0 = this.links[link_b1.next].vertex;
57 | var vb1 = link_b1.vertex;
58 |
59 | var bb = vb0.point - vb1.point;
60 | var bp = vb1.point - v.point;
61 |
62 | var bpb = bp.CrossProduct(bb);
63 | if (bpb > 0) {
64 | return false;
65 | }
66 |
67 | var linkIndex = this.links.Count;
68 | var link_p = new Link(link_a1.self, linkIndex, link_b1.self, v);
69 |
70 | link_a1.next = linkIndex;
71 | link_b1.prev = linkIndex;
72 |
73 | this.links.Add(link_p);
74 | this.links[link_a1.self] = link_a1;
75 | this.links[link_b1.self] = link_b1;
76 |
77 | var n0 = triangle.FindNeighbor(vb1.index);
78 | if (n0 >= 0) {
79 | var edge0 = new Edge(triangle.index, n0, edge.a, linkIndex);
80 | this.edges.Add(edge0);
81 | }
82 |
83 | var n1 = triangle.FindNeighbor(va1.index);
84 | if (n1 >= 0) {
85 | var edge1 = new Edge(triangle.index, n1, linkIndex, edge.b);
86 | this.edges.Add(edge1);
87 | }
88 |
89 | return true;
90 | }
91 |
92 | internal ConvexPolygon(Triangle triangle) {
93 | const int capacity = 16;
94 |
95 | this.links = new DynamicArray(capacity, Allocator.Temp);
96 | this.links.Add(new Link(2, 0, 1, triangle.Vertex(0)));
97 | this.links.Add(new Link(0, 1, 2, triangle.Vertex(1)));
98 | this.links.Add(new Link(1, 2, 0, triangle.Vertex(2)));
99 |
100 | this.edges = new DynamicArray(capacity, Allocator.Temp);
101 |
102 | var ab = triangle.Neighbor(2);
103 | if (ab >= 0) {
104 | this.edges.Add(new Edge(triangle.index, ab, 0, 1));
105 | }
106 |
107 | var bc = triangle.Neighbor(0);
108 | if (bc >= 0) {
109 | this.edges.Add(new Edge(triangle.index, bc, 1, 2));
110 | }
111 |
112 | var ca = triangle.Neighbor(1);
113 | if (ca >= 0) {
114 | this.edges.Add(new Edge(triangle.index, ca, 2, 0));
115 | }
116 | }
117 |
118 | internal void Dispose() {
119 | this.edges.Dispose();
120 | this.links.Dispose();
121 | }
122 | }
123 |
124 | public static class Polygons {
125 |
126 | public static List ConvexPolygons(this PlainShape self, Allocator allocator, IntGeom intGeom) {
127 | var delaunay = self.Delaunay(Allocator.Temp);
128 | var list = delaunay.ConvexPolygons(intGeom, allocator);
129 | delaunay.Dispose();
130 | return list;
131 | }
132 |
133 | public static List ConvexPolygons(this Delaunay self, IntGeom intGeom, Allocator allocator) {
134 | int n = self.triangles.Count;
135 | var dynamicList = new DynamicList(self.points.Count, n >> 1, allocator);
136 | var visited = new NativeArray(n, Allocator.Temp);
137 |
138 | for (int i = 0; i < n; ++i) {
139 | if (visited[i]) {
140 | continue;
141 | }
142 |
143 | var first = self.triangles[i];
144 | visited[i] = true;
145 | var convexPolygon = new ConvexPolygon(first);
146 |
147 | while (convexPolygon.edges.Count > 0) {
148 | var edge = convexPolygon.edges.Last();
149 | convexPolygon.edges.RemoveLast();
150 | if (visited[edge.neighbor]) {
151 | continue;
152 | }
153 |
154 | var next = self.triangles[edge.neighbor];
155 | if (convexPolygon.Add(edge, next)) {
156 | visited[edge.neighbor] = true;
157 | }
158 | }
159 |
160 | var iPoints = convexPolygon.Points(Allocator.Temp);
161 | var points = intGeom.Float(iPoints, Allocator.Temp);
162 | var polygon = new Polygon(points, allocator);
163 | dynamicList.Add(polygon);
164 |
165 | iPoints.Dispose();
166 | points.Dispose();
167 | convexPolygon.Dispose();
168 | }
169 |
170 | visited.Dispose();
171 |
172 | return dynamicList.Convert();
173 | }
174 | }
175 |
176 | }
--------------------------------------------------------------------------------
/Tests/Editor/Triangulation/Data/MonotoneTests.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 |
3 | namespace Tests.Triangulation.Data {
4 |
5 | public struct MonotoneTests {
6 |
7 | public static readonly Vector2[][] data = {
8 | // test 0
9 | new Vector2[] {
10 | new Vector2(-15, -15),
11 | new Vector2(-15, 15),
12 | new Vector2(15, 15),
13 | new Vector2(15, -15)
14 | },
15 | // test 1
16 | new Vector2[] {
17 | new Vector2(-15, 10),
18 | new Vector2(5, 10),
19 | new Vector2(15, -10),
20 | new Vector2(-5, -10)
21 | },
22 | // test 2
23 | new Vector2[] {
24 | new Vector2(-15, -15),
25 | new Vector2(-25, 0),
26 | new Vector2(-15, 15),
27 | new Vector2(15, 15),
28 | new Vector2(15, -15)
29 | },
30 | // test 3
31 | new Vector2[] {
32 | new Vector2(-5, -15),
33 | new Vector2(-10, 0),
34 | new Vector2( 0, 15),
35 | new Vector2(10, 5),
36 | new Vector2( 5, -10)
37 | },
38 | // test 4
39 | new Vector2[] {
40 | new Vector2(0, -5),
41 | new Vector2(0, 0),
42 | new Vector2(10, -10),
43 | new Vector2(-10, -10)
44 | },
45 | // test 5
46 | new Vector2[] {
47 | new Vector2(-15, -15),
48 | new Vector2(-15, 0),
49 | new Vector2(0, 0),
50 | new Vector2(0, 15),
51 | new Vector2(15, -15)
52 | },
53 | // test 6
54 | new Vector2[] {
55 | new Vector2(-15, -15),
56 | new Vector2(-15, 0),
57 | new Vector2(-1, 20),
58 | new Vector2(0, 5),
59 | new Vector2(15, -15)
60 | },
61 | // test 7
62 | new Vector2[] {
63 | new Vector2(-10, 10),
64 | new Vector2(-5, 5),
65 | new Vector2(10, 20),
66 | new Vector2(20, 20),
67 | new Vector2(25, 20),
68 | new Vector2(25, -5),
69 | new Vector2(10, -5),
70 | new Vector2(10, -10),
71 | new Vector2(-10, -10)
72 | },
73 | // test 8
74 | new Vector2[] {
75 | new Vector2(-10, 10),
76 | new Vector2(-5, 15),
77 | new Vector2(10, 20),
78 | new Vector2(20, 20),
79 | new Vector2(25, 20),
80 | new Vector2(25, -5),
81 | new Vector2(10, -5),
82 | new Vector2(10, -10),
83 | new Vector2(-10, -10)
84 | },
85 | // test 9
86 | new Vector2[] {
87 | new Vector2(-10, 10),
88 | new Vector2( -5, 5),
89 | new Vector2( 10, 20),
90 | new Vector2( 15, 10),
91 | new Vector2( 25, 20),
92 | new Vector2( 25, 0),
93 | new Vector2(10, 0),
94 | new Vector2(10, -10),
95 | new Vector2(-10, -10)
96 | },
97 | // test 10
98 | new Vector2[] {
99 | new Vector2(-10, 10),
100 | new Vector2( -5, -5),
101 | new Vector2( 10, 20),
102 | new Vector2( 15, 10),
103 | new Vector2( 25, 20),
104 | new Vector2(25, 0),
105 | new Vector2(10, 0),
106 | new Vector2(10, -10),
107 | new Vector2(-10, -10)
108 | },
109 | // test 11
110 | new Vector2[] {
111 | new Vector2(-10, 10),
112 | new Vector2( 10, 10),
113 | new Vector2( 10, 20),
114 | new Vector2( 15, 10),
115 | new Vector2( 25, 20),
116 | new Vector2(25, 0),
117 | new Vector2(10, 0),
118 | new Vector2(10, -10),
119 | new Vector2(-10, -10)
120 | },
121 | // test 12
122 | new Vector2[] {
123 | new Vector2(-35, 5),
124 | new Vector2(-20, 10),
125 | new Vector2(-18, 20),
126 | new Vector2(0, 20),
127 | new Vector2(5, 10),
128 | new Vector2(15, 5),
129 | new Vector2(20, 10),
130 | new Vector2(35, 0),
131 | new Vector2(25, -10),
132 | new Vector2(10, -4),
133 | new Vector2(-5, -15),
134 | new Vector2(-5, -20),
135 | new Vector2(-15, -25),
136 | new Vector2(-20, -10),
137 | new Vector2(-30, -5)
138 | },
139 | // test 13
140 | new Vector2[] {
141 | new Vector2(-35, 5),
142 | new Vector2(-20, 10),
143 | new Vector2(-10, 20),
144 | new Vector2(0, 20),
145 | new Vector2(5, 10),
146 | new Vector2(15, 5),
147 | new Vector2(20, 10),
148 | new Vector2(35, 0),
149 | new Vector2(25, -10),
150 | new Vector2(10, -4),
151 | new Vector2(-5, -15),
152 | new Vector2(-5, -20),
153 | new Vector2(-15, -25),
154 | new Vector2(-20, -10),
155 | new Vector2(-30, -5)
156 | },
157 | // test 14
158 | new Vector2[] {
159 | new Vector2(-10, -10),
160 | new Vector2(-10, -5),
161 | new Vector2(-10, 0),
162 | new Vector2(-10, 5),
163 | new Vector2(-10, 10),
164 | new Vector2(10, 10),
165 | new Vector2(10, 5),
166 | new Vector2(10, 0),
167 | new Vector2(10, -5),
168 | new Vector2(10, -10)
169 | },
170 | // test 15
171 | new Vector2[] {
172 | new Vector2(-20, 0),
173 | new Vector2(-15, 15),
174 | new Vector2(-10, 20),
175 | new Vector2( -5, 15),
176 | new Vector2( 0, 20),
177 | new Vector2( 5, 15),
178 | new Vector2( 10, 20),
179 | new Vector2( 15, 15),
180 | new Vector2( 25, 0),
181 | new Vector2( 20, -15),
182 | new Vector2( 15, -20),
183 | new Vector2( 10, -15),
184 | new Vector2( 5, -20),
185 | new Vector2( 0, -15),
186 | new Vector2( -5, -20),
187 | new Vector2(-10, -15)
188 | },
189 | // test 16
190 | new Vector2[] {
191 | new Vector2(-20, 5),
192 | new Vector2(-10, 10),
193 | new Vector2( -5, 20),
194 | new Vector2( 0, 25),
195 | new Vector2( 5, 15),
196 | new Vector2( 10, 0),
197 | new Vector2( 15, 5),
198 | new Vector2( 20, -5),
199 | new Vector2( 15, -15),
200 | new Vector2( 5, -25),
201 | new Vector2( 0, -15),
202 | new Vector2(-10, -10),
203 | new Vector2(-15, -5),
204 | },
205 | // test 17
206 | new Vector2[] {
207 | new Vector2(-35, 5),
208 | new Vector2(-13.5f, 8),
209 | new Vector2(-9.5f, 20),
210 | new Vector2(3, 20),
211 | new Vector2(8.5f, 11),
212 | new Vector2( 15, 5),
213 | new Vector2( 32, 14.5f),
214 | new Vector2( 35, 0),
215 | new Vector2( 25, -10),
216 | new Vector2( 0, 1.5f),
217 | new Vector2(-0.5f, -12.5f),
218 | new Vector2( -5, -20),
219 | new Vector2(-7.5f, 2.5f),
220 | new Vector2(-31, -4)
221 | },
222 | // test 18
223 | new Vector2[] {
224 | new Vector2(-10, 5),
225 | new Vector2( -5, 5),
226 | new Vector2( 0, 0),
227 | new Vector2( 5, 5),
228 | new Vector2( 10, 5),
229 | new Vector2( 10, -5),
230 | new Vector2( 5, -5),
231 | new Vector2( 0, 0),
232 | new Vector2( -5, -5),
233 | new Vector2(-10, -5)
234 | }
235 | };
236 | }
237 | }
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/Delaunay/SliceBuffer.cs:
--------------------------------------------------------------------------------
1 | using Unity.Collections;
2 |
3 | namespace iShape.Triangulation.Shape.Delaunay {
4 |
5 | internal struct SliceBuffer {
6 |
7 | private readonly int vertexCount;
8 | private NativeArray sides;
9 | private NativeArray vertexMark;
10 |
11 | internal SliceBuffer(int vertexCount, NativeArray slices, Allocator allocator) {
12 | this.vertexCount = vertexCount;
13 | this.vertexMark = new NativeArray(vertexCount, allocator);
14 | int n = slices.Length;
15 | this.sides = new NativeArray(n, allocator);
16 |
17 | for(int i = 0; i < n; ++i) {
18 | var slice = slices[i];
19 |
20 | vertexMark[slice.a] = true;
21 | vertexMark[slice.b] = true;
22 |
23 | int id;
24 |
25 | if(slice.a < slice.b) {
26 | id = slice.a * vertexCount + slice.b;
27 | } else {
28 | id = slice.b * vertexCount + slice.a;
29 | }
30 | sides[i] = new Side(id, -1, -1);
31 | }
32 |
33 | Sort(sides);
34 | }
35 |
36 | public void Dispose() {
37 | this.sides.Dispose();
38 | this.vertexMark.Dispose();
39 | }
40 |
41 | public void AddConnections(NativeArray triangles) {
42 | int n = triangles.Length;
43 |
44 | for(int i = 0; i < n; ++i) {
45 | var triangle = triangles[i];
46 | int a = triangle.vA.index;
47 | int b = triangle.vB.index;
48 | int c = triangle.vC.index;
49 |
50 | int sideIndex = this.Find(a, b);
51 | if(sideIndex >= 0) {
52 | var side = this.sides[sideIndex];
53 | if(side.IsEmpty) {
54 | side.triangle = i;
55 | side.edge = 2;
56 | this.sides[sideIndex] = side;
57 | } else {
58 | triangle.SetNeighbor(2, side.triangle);
59 | var neighbor = triangles[side.triangle];
60 | neighbor.SetNeighbor(side.edge, i);
61 | triangles[side.triangle] = neighbor;
62 | triangles[i] = triangle;
63 | }
64 | }
65 |
66 | sideIndex = this.Find(a, c);
67 | if(sideIndex >= 0) {
68 | var side = this.sides[sideIndex];
69 | if(side.IsEmpty) {
70 | side.triangle = i;
71 | side.edge = 1;
72 | this.sides[sideIndex] = side;
73 | } else {
74 | triangle.SetNeighbor(1, side.triangle);
75 | var neighbor = triangles[side.triangle];
76 | neighbor.SetNeighbor(side.edge, i);
77 | triangles[side.triangle] = neighbor;
78 | triangles[i] = triangle;
79 | }
80 | }
81 |
82 | sideIndex = this.Find(b, c);
83 | if(sideIndex >= 0) {
84 | var side = this.sides[sideIndex];
85 | if(side.IsEmpty) {
86 | side.triangle = i;
87 | side.edge = 0;
88 | this.sides[sideIndex] = side;
89 | } else {
90 | triangle.SetNeighbor(0, side.triangle);
91 | var neighbor = triangles[side.triangle];
92 | neighbor.SetNeighbor(side.edge, i);
93 | triangles[side.triangle] = neighbor;
94 | triangles[i] = triangle;
95 | }
96 | }
97 | }
98 | }
99 |
100 | private int Find(int a, int b) {
101 | if(!vertexMark[a] || !vertexMark[b]) {
102 | return -1;
103 | }
104 | int id;
105 | if(a < b) {
106 | id = a * vertexCount + b;
107 | } else {
108 | id = b * vertexCount + a;
109 | }
110 |
111 | var left = 0;
112 | var right = sides.Length - 1;
113 |
114 | do {
115 | int k;
116 | if(left + 1 < right) {
117 | k = (left + right) >> 1;
118 | } else {
119 | do {
120 | if(sides[left].id == id) {
121 | return left;
122 | }
123 | ++left;
124 | } while(left <= right);
125 | return -1;
126 | }
127 |
128 | int e = sides[k].id;
129 | if(e > id) {
130 | right = k;
131 | } else if(e < id) {
132 | left = k;
133 | } else {
134 | return k;
135 | }
136 | } while(true);
137 | }
138 |
139 | private static void Sort(NativeArray array) {
140 | int n = array.Length;
141 | int r = 2;
142 | int rank = 1;
143 |
144 | while(r <= n) {
145 | rank = r;
146 | r <<= 1;
147 | }
148 | rank -= 1;
149 |
150 | int jEnd = rank;
151 |
152 | int jStart = ((jEnd + 1) >> 1) - 1;
153 |
154 |
155 | while(jStart >= 0) {
156 | int k = jStart;
157 | while(k < jEnd) {
158 | int j = k;
159 |
160 | var a = array[j];
161 | bool fallDown;
162 | do {
163 | fallDown = false;
164 |
165 | int j0 = (j << 1) + 1;
166 | int j1 = j0 + 1;
167 |
168 | if(j1 < n) {
169 | var a0 = array[j0];
170 | var a1 = array[j1];
171 |
172 | if(a.id < a0.id || a.id < a1.id) {
173 | if(a0.id > a1.id) {
174 | array[j0] = a;
175 | array[j] = a0;
176 | j = j0;
177 | } else {
178 | array[j1] = a;
179 | array[j] = a1;
180 | j = j1;
181 | }
182 | fallDown = j < rank;
183 | }
184 | } else if(j0 < n) {
185 | var ax = array[j];
186 | var a0 = array[j0];
187 | if(ax.id < a0.id) {
188 | array[j0] = ax;
189 | array[j] = a0;
190 | }
191 | }
192 |
193 | } while(fallDown);
194 | ++k;
195 | }
196 |
197 | jEnd = jStart;
198 | jStart = ((jEnd + 1) >> 1) - 1;
199 | }
200 |
201 | while(n > 0) {
202 | int m = n - 1;
203 |
204 | var a = array[m];
205 | array[m] = array[0];
206 | array[0] = a;
207 |
208 | int j = 0;
209 | bool fallDown;
210 | do {
211 | fallDown = false;
212 |
213 | int j0 = (j << 1) + 1;
214 | int j1 = j0 + 1;
215 |
216 | if(j1 < m) {
217 | var a0 = array[j0];
218 | var a1 = array[j1];
219 | fallDown = a.id < a0.id || a.id < a1.id;
220 |
221 | if(fallDown) {
222 | if(a0.id > a1.id) {
223 | array[j0] = a;
224 | array[j] = a0;
225 | j = j0;
226 | } else {
227 | array[j1] = a;
228 | array[j] = a1;
229 | j = j1;
230 | }
231 | }
232 | } else if(j0 < m) {
233 | var ax = array[j];
234 | var a0 = array[j0];
235 | if(ax.id < a0.id) {
236 | array[j0] = ax;
237 | array[j] = a0;
238 | }
239 | }
240 |
241 | } while(fallDown);
242 |
243 | n = m;
244 | }
245 | }
246 | }
247 | }
248 |
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/TriangulationExt.cs:
--------------------------------------------------------------------------------
1 | using iShape.Triangulation.Util;
2 | using iShape.Geometry;
3 | using iShape.Geometry.Container;
4 | using Unity.Collections;
5 | using UnityEngine;
6 |
7 | namespace iShape.Triangulation.Shape {
8 |
9 | public static class TriangulationExt {
10 |
11 | public static Mesh Triangulate(this PlainShape shape, IntGeom iGeom) {
12 | int n = shape.points.Length;
13 | var vertices = new Vector3[n];
14 | for (int i = 0; i < n; ++i) {
15 | var v = iGeom.Float(shape.points[i]);
16 | vertices[i] = new Vector3(v.x, v.y, 0);
17 | }
18 | var extraPoints = new NativeArray(0, Allocator.Temp);
19 | var nTriangles = shape.Triangulate(extraPoints, Allocator.Temp);
20 | extraPoints.Dispose();
21 |
22 | var mesh = new Mesh {
23 | vertices = vertices,
24 | triangles = nTriangles.ToArray()
25 | };
26 |
27 | nTriangles.Dispose();
28 |
29 | return mesh;
30 | }
31 |
32 | public static NativeArray Triangulate(this PlainShape shape, Allocator allocator) {
33 | var extraPoints = new NativeArray(0, Allocator.Temp);
34 | var triangles = Triangulate(shape, extraPoints, allocator);
35 | extraPoints.Dispose();
36 | return triangles;
37 | }
38 |
39 | public static NativeArray Triangulate(this PlainShape shape, NativeArray extraPoints, Allocator allocator) {
40 | var layout = shape.Split(0, extraPoints, Allocator.Temp);
41 | int totalCount = shape.points.Length + ((shape.layouts.Length - 2) << 1);
42 |
43 | int trianglesCount = 3 * totalCount;
44 |
45 | var triangles = new NativeArray(trianglesCount, allocator);
46 | int counter = 0;
47 | for(int i = 0; i < layout.indices.Length; ++i) {
48 | int index = layout.indices[i];
49 | Triangulate(index, ref counter, layout.links, triangles);
50 | }
51 |
52 | layout.Dispose();
53 |
54 | if(counter == trianglesCount) {
55 | return triangles;
56 | } else {
57 | var newTriangles = new NativeArray(counter, allocator);
58 | newTriangles.Slice(0, counter).CopyFrom(triangles.Slice(0, counter));
59 | triangles.Dispose();
60 | return newTriangles;
61 | }
62 | }
63 |
64 | public static void Triangulate(int index, ref int counter, NativeArray links, NativeArray triangles) {
65 | var c = links[index];
66 |
67 | var a0 = links[c.next];
68 | var b0 = links[c.prev];
69 |
70 | while(a0.self != b0.self) {
71 | var a1 = links[a0.next];
72 | var b1 = links[b0.prev];
73 |
74 |
75 | var aBit0 = a0.vertex.point.BitPack;
76 | var aBit1 = a1.vertex.point.BitPack;
77 | if(aBit1 < aBit0) {
78 | aBit1 = aBit0;
79 | }
80 |
81 | var bBit0 = b0.vertex.point.BitPack;
82 | var bBit1 = b1.vertex.point.BitPack;
83 | if(bBit1 < bBit0) {
84 | bBit1 = bBit0;
85 | }
86 |
87 | if(aBit0 <= bBit1 && bBit0 <= aBit1) {
88 | if(IntTriangle.IsNotLine(c.vertex.point, a0.vertex.point, b0.vertex.point)) {
89 | triangles[counter++] = c.vertex.index;
90 | triangles[counter++] = a0.vertex.index;
91 | triangles[counter++] = b0.vertex.index;
92 | }
93 |
94 | a0.prev = b0.self;
95 | b0.next = a0.self;
96 | links[a0.self] = a0;
97 | links[b0.self] = b0;
98 |
99 |
100 | if(bBit0 < aBit0) {
101 | c = b0;
102 | b0 = b1;
103 | } else {
104 | c = a0;
105 | a0 = a1;
106 | }
107 | } else {
108 | if(aBit1 < bBit1) {
109 | var cx = c;
110 | var ax0 = a0;
111 | var ax1 = a1;
112 | long ax1Bit = long.MinValue;
113 | do {
114 | var orientation = IntTriangle.GetOrientation(cx.vertex.point, ax0.vertex.point, ax1.vertex.point);
115 | switch(orientation) {
116 | case IntTriangle.Orientation.clockWise:
117 | triangles[counter++] = cx.vertex.index;
118 | triangles[counter++] = ax0.vertex.index;
119 | triangles[counter++] = ax1.vertex.index;
120 | goto case IntTriangle.Orientation.line;
121 | case IntTriangle.Orientation.line:
122 | ax1.prev = cx.self;
123 | cx.next = ax1.self;
124 | links[cx.self] = cx;
125 | links[ax1.self] = ax1;
126 |
127 | if(cx.self != c.self) {
128 | // move back
129 | ax0 = cx;
130 | cx = links[cx.prev];
131 | continue;
132 | } else {
133 | // move forward
134 | ax0 = ax1;
135 | ax1 = links[ax1.next];
136 | break;
137 | }
138 | case IntTriangle.Orientation.counterClockWise:
139 | cx = ax0;
140 | ax0 = ax1;
141 | ax1 = links[ax1.next];
142 | break;
143 | }
144 | ax1Bit = ax1.vertex.point.BitPack;
145 | } while(ax1Bit < bBit0);
146 | } else {
147 | var cx = c;
148 | var bx0 = b0;
149 | var bx1 = b1;
150 | long bx1Bit = long.MinValue;
151 | do {
152 | var orientation = IntTriangle.GetOrientation(cx.vertex.point, bx1.vertex.point, bx0.vertex.point);
153 | switch(orientation) {
154 | case IntTriangle.Orientation.clockWise:
155 | triangles[counter++] = cx.vertex.index;
156 | triangles[counter++] = bx1.vertex.index;
157 | triangles[counter++] = bx0.vertex.index;
158 | goto case IntTriangle.Orientation.line;
159 | case IntTriangle.Orientation.line:
160 | bx1.next = cx.self;
161 | cx.prev = bx1.self;
162 | links[cx.self] = cx;
163 | links[bx1.self] = bx1;
164 |
165 | if(cx.self != c.self) {
166 | // move back
167 | bx0 = cx;
168 | cx = links[cx.next];
169 | continue;
170 | } else {
171 | // move forward
172 | bx0 = bx1;
173 | bx1 = links[bx0.prev];
174 | break;
175 | }
176 | case IntTriangle.Orientation.counterClockWise:
177 | cx = bx0;
178 | bx0 = bx1;
179 | bx1 = links[bx1.prev];
180 | break;
181 | }
182 | bx1Bit = bx1.vertex.point.BitPack;
183 | } while(bx1Bit < aBit0);
184 | }
185 |
186 | c = links[c.self];
187 | a0 = links[c.next];
188 | b0 = links[c.prev];
189 |
190 |
191 | aBit0 = a0.vertex.point.BitPack;
192 | bBit0 = b0.vertex.point.BitPack;
193 |
194 | if(IntTriangle.IsNotLine(c.vertex.point, a0.vertex.point, b0.vertex.point)) {
195 | triangles[counter++] = c.vertex.index;
196 | triangles[counter++] = a0.vertex.index;
197 | triangles[counter++] = b0.vertex.index;
198 | }
199 | a0.prev = b0.self;
200 | b0.next = a0.self;
201 | links[a0.self] = a0;
202 | links[b0.self] = b0;
203 |
204 | if(bBit0 < aBit0) {
205 | c = b0;
206 | b0 = links[b0.prev];
207 | } else {
208 | c = a0;
209 | a0 = links[a0.next];
210 | }
211 |
212 | } //while
213 | }
214 | }
215 | }
216 |
217 | }
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/Delaunay/Triangulation.cs:
--------------------------------------------------------------------------------
1 | using iShape.Triangulation.Util;
2 | using iShape.Geometry;
3 | using iShape.Geometry.Container;
4 | using Unity.Collections;
5 | using UnityEngine;
6 |
7 | namespace iShape.Triangulation.Shape.Delaunay {
8 |
9 | public static class Triangulation {
10 |
11 | public static Mesh DelaunayTriangulate(this PlainShape shape, IntGeom iGeom) {
12 | int n = shape.points.Length;
13 | var vertices = new Vector3[n];
14 | for (int i = 0; i < n; ++i) {
15 | var v = iGeom.Float(shape.points[i]);
16 | vertices[i] = new Vector3(v.x, v.y, 0);
17 | }
18 | var extraPoints = new NativeArray(0, Allocator.Temp);
19 | var delaunay = shape.Delaunay(0, extraPoints, Allocator.Temp);
20 | extraPoints.Dispose();
21 |
22 | var nTriangles = delaunay.Indices(Allocator.Temp);
23 | delaunay.Dispose();
24 |
25 | var mesh = new Mesh {
26 | vertices = vertices,
27 | triangles = nTriangles.ToArray()
28 | };
29 |
30 | nTriangles.Dispose();
31 |
32 | return mesh;
33 | }
34 |
35 | public static NativeArray DelaunayTriangulate(this PlainShape shape, Allocator allocator) {
36 | var extraPoints = new NativeArray(0, Allocator.Temp);
37 | var delaunay = shape.Delaunay(0, extraPoints, allocator);
38 | extraPoints.Dispose();
39 |
40 | var triangles = delaunay.Indices(allocator);
41 | delaunay.Dispose();
42 |
43 | return triangles;
44 | }
45 |
46 | public static NativeArray DelaunayTriangulate(this PlainShape shape, Allocator allocator, NativeArray extraPoints) {
47 | var delaunay = shape.Delaunay(0, extraPoints, allocator);
48 | var triangles = delaunay.Indices(allocator);
49 | delaunay.Dispose();
50 |
51 | return triangles;
52 | }
53 |
54 | public static Delaunay Delaunay(this PlainShape shape, long maxEdge, NativeArray extraPoints, Allocator allocator) {
55 | var layout = shape.Split(maxEdge, extraPoints, Allocator.Temp);
56 |
57 | int holesCount = shape.layouts.Length;
58 | int totalCount = layout.pathCount + 2 * layout.extraCount + holesCount * 2 - 2;
59 |
60 | var triangleStack = new TriangleStack(totalCount, allocator);
61 |
62 | for(int i = 0; i < layout.indices.Length; ++i) {
63 | int index = layout.indices[i];
64 | Triangulate(index, layout.links, ref triangleStack);
65 | triangleStack.Reset();
66 | }
67 |
68 | var triangles = triangleStack.Convert();
69 |
70 | var sliceBuffer = new SliceBuffer(layout.links.Length, layout.slices, Allocator.Temp);
71 | sliceBuffer.AddConnections(triangles);
72 |
73 | sliceBuffer.Dispose();
74 |
75 | Delaunay delaunay;
76 | if (extraPoints.Length == 0 && maxEdge == 0) {
77 | delaunay = new Delaunay(shape.points, triangles, allocator);
78 | } else {
79 | var points = new NativeArray(layout.links.Length, Allocator.Temp);
80 | for(int i = 0; i < layout.links.Length; ++i) {
81 | var link = layout.links[i];
82 | points[link.vertex.index] = link.vertex.point;
83 | }
84 | delaunay = new Delaunay(points, triangles, allocator);
85 | points.Dispose();
86 | }
87 |
88 | triangles.Dispose();
89 |
90 | layout.Dispose();
91 |
92 | delaunay.Build();
93 |
94 | return delaunay;
95 | }
96 |
97 | public static Delaunay Delaunay(this PlainShape shape, long maxEdge, Allocator allocator) {
98 | var extraPoints = new NativeArray(0, Allocator.Temp);
99 | var delaunay = shape.Delaunay(maxEdge, extraPoints, allocator);
100 | extraPoints.Dispose();
101 | return delaunay;
102 | }
103 |
104 | public static Delaunay Delaunay(this PlainShape shape, Allocator allocator) {
105 | var extraPoints = new NativeArray(0, Allocator.Temp);
106 | var delaunay = shape.Delaunay(0, extraPoints, allocator);
107 | extraPoints.Dispose();
108 | return delaunay;
109 | }
110 |
111 | private static void Triangulate(int index, NativeArray links, ref TriangleStack triangleStack) {
112 |
113 | var c = links[index];
114 |
115 | var a0 = links[c.next];
116 | var b0 = links[c.prev];
117 |
118 | while(a0.self != b0.self) {
119 | var a1 = links[a0.next];
120 | var b1 = links[b0.prev];
121 |
122 |
123 | var aBit0 = a0.vertex.point.BitPack;
124 | var aBit1 = a1.vertex.point.BitPack;
125 | if(aBit1 < aBit0) {
126 | aBit1 = aBit0;
127 | }
128 |
129 | var bBit0 = b0.vertex.point.BitPack;
130 | var bBit1 = b1.vertex.point.BitPack;
131 | if(bBit1 < bBit0) {
132 | bBit1 = bBit0;
133 | }
134 |
135 | if(aBit0 <= bBit1 && bBit0 <= aBit1) {
136 | triangleStack.Add(c.vertex, a0.vertex, b0.vertex);
137 |
138 | a0.prev = b0.self;
139 | b0.next = a0.self;
140 | links[a0.self] = a0;
141 | links[b0.self] = b0;
142 |
143 | if(bBit0 < aBit0) {
144 | c = b0;
145 | b0 = b1;
146 | } else {
147 | c = a0;
148 | a0 = a1;
149 | }
150 |
151 | } else {
152 | if(aBit1 < bBit1) {
153 | var cx = c;
154 | var ax0 = a0;
155 | var ax1 = a1;
156 | long ax1Bit;
157 | do {
158 | var isCCW_or_Line = IntTriangle.IsCCW_or_Line(cx.vertex.point, ax0.vertex.point, ax1.vertex.point);
159 |
160 | if(isCCW_or_Line) {
161 | triangleStack.Add(ax0.vertex, ax1.vertex, cx.vertex);
162 |
163 | ax1.prev = cx.self;
164 | cx.next = ax1.self;
165 | links[cx.self] = cx;
166 | links[ax1.self] = ax1;
167 |
168 | if(cx.self != c.self) {
169 | // move back
170 | ax0 = cx;
171 | cx = links[cx.prev];
172 | } else {
173 | // move forward
174 | ax0 = ax1;
175 | ax1 = links[ax1.next];
176 | }
177 | } else {
178 | cx = ax0;
179 | ax0 = ax1;
180 | ax1 = links[ax1.next];
181 | }
182 | ax1Bit = ax1.vertex.point.BitPack;
183 | } while(ax1Bit < bBit0);
184 | } else {
185 | var cx = c;
186 | var bx0 = b0;
187 | var bx1 = b1;
188 | long bx1Bit;
189 | do {
190 | bool isCCW_or_Line = IntTriangle.IsCCW_or_Line(cx.vertex.point, bx1.vertex.point, bx0.vertex.point);
191 | if(isCCW_or_Line) {
192 | triangleStack.Add(bx0.vertex, cx.vertex, bx1.vertex);
193 |
194 | bx1.next = cx.self;
195 | cx.prev = bx1.self;
196 | links[cx.self] = cx;
197 | links[bx1.self] = bx1;
198 |
199 | if(cx.self != c.self) {
200 | // move back
201 | bx0 = cx;
202 | cx = links[cx.next];
203 | } else {
204 | // move forward
205 | bx0 = bx1;
206 | bx1 = links[bx0.prev];
207 | }
208 | } else {
209 | cx = bx0;
210 | bx0 = bx1;
211 | bx1 = links[bx1.prev];
212 | }
213 | bx1Bit = bx1.vertex.point.BitPack;
214 | } while(bx1Bit < aBit0);
215 | }
216 |
217 | c = links[c.self];
218 | a0 = links[c.next];
219 | b0 = links[c.prev];
220 |
221 | aBit0 = a0.vertex.point.BitPack;
222 | bBit0 = b0.vertex.point.BitPack;
223 |
224 | triangleStack.Add(c.vertex, a0.vertex, b0.vertex);
225 |
226 | a0.prev = b0.self;
227 | b0.next = a0.self;
228 | links[a0.self] = a0;
229 | links[b0.self] = b0;
230 |
231 | if(bBit0 < aBit0) {
232 | c = b0;
233 | b0 = links[b0.prev];
234 | } else {
235 | c = a0;
236 | a0 = links[a0.next];
237 | }
238 |
239 | }
240 | } // while
241 | }
242 | }
243 |
244 | }
245 |
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/Delaunay/Tessellation.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using iShape.Geometry;
3 | using iShape.Geometry.Container;
4 | using iShape.Geometry.Extension;
5 | using Unity.Collections;
6 | using UnityEngine;
7 |
8 | namespace iShape.Triangulation.Shape.Delaunay {
9 |
10 | internal readonly struct Validator {
11 | private static readonly float mergeCos = Mathf.Cos(0.8f * Mathf.PI);
12 | internal static readonly float sqrMergeCos = mergeCos * mergeCos;
13 | private readonly float maxArea;
14 | private readonly IntGeom intGeom;
15 |
16 | internal Validator(IntGeom intGeom, float maxArea) {
17 | this.intGeom = intGeom;
18 | this.maxArea = 2f * maxArea;
19 | }
20 |
21 | internal int TestRegular(Triangle triangle) {
22 | var a = intGeom.Float(triangle.vA.point);
23 | var b = intGeom.Float(triangle.vB.point);
24 | var c = intGeom.Float(triangle.vC.point);
25 |
26 | var ab = a.SqrDistance(b);
27 | var ca = c.SqrDistance(a);
28 | var bc = b.SqrDistance(c);
29 |
30 | float s0 = a.x * (c.y - b.y) + b.x * (a.y - c.y) + c.x * (b.y - a.y);
31 | float s1;
32 |
33 | int k;
34 | float sCos;
35 |
36 | if (ab >= bc + ca) {
37 | // c, ab
38 | k = 2;
39 | float l = bc + ca - ab;
40 | sCos = l * l / (4 * bc * ca);
41 | s1 = s0 / (1 - sCos);
42 | } else if (bc >= ca + ab) {
43 | // a, bc
44 | k = 0;
45 | float l = ca + ab - bc;
46 | sCos = l * l / (4 * ca * ab);
47 | s1 = s0 / (1 - sCos);
48 | } else if (ca >= bc + ab) {
49 | // b, ca
50 | k = 1;
51 | float l = bc + ab - ca;
52 | sCos = l * l / (4 * bc * ab);
53 | s1 = s0 / (1 - sCos);
54 | } else {
55 | if (ab >= bc && ab >= ca) {
56 | k = 2;
57 | } else if (bc >= ca) {
58 | k = 0;
59 | } else {
60 | k = 1;
61 | }
62 |
63 | s1 = s0;
64 | }
65 |
66 | if (s1 > maxArea) {
67 | return k;
68 | }
69 |
70 | return -1;
71 | }
72 |
73 | internal static float SqrCos(IntVector a, IntVector b, IntVector c) {
74 | long ab = a.SqrDistance(b);
75 | long ca = c.SqrDistance(a);
76 | long bc = b.SqrDistance(c);
77 |
78 | if (ab >= bc + ca) {
79 | float aa = bc;
80 | float bb = ca;
81 | float cc = ab;
82 |
83 | float l = aa + bb - cc;
84 | return l * l / (4 * aa * bb);
85 | }
86 |
87 | return 0f;
88 | }
89 | }
90 |
91 | public static class TessellationExtension {
92 | public static Delaunay Tessellate(ref this PlainShape self, Allocator allocator, IntGeom intGeom, float maxEdge, NativeArray extraPoints, float maxArea = 0) {
93 | long iEdge = intGeom.Int(maxEdge);
94 | var delaunay = self.Delaunay(iEdge, extraPoints, allocator);
95 |
96 | float area;
97 | if (maxArea > 0) {
98 | area = maxArea;
99 | } else {
100 | area = 0.4f * maxEdge * maxEdge;
101 | }
102 | delaunay.Tessellate(intGeom, area);
103 | return delaunay;
104 | }
105 |
106 | public static Delaunay Tessellate(ref this PlainShape self, Allocator allocator, IntGeom intGeom, float maxEdge, float maxArea = 0) {
107 | long iEdge = intGeom.Int(maxEdge);
108 | var extraPoints = new NativeArray(0, Allocator.Temp);
109 | var delaunay = self.Delaunay(iEdge, extraPoints, allocator);
110 | extraPoints.Dispose();
111 |
112 | float area;
113 | if (maxArea > 0) {
114 | area = maxArea;
115 | } else {
116 | area = 0.4f * maxEdge * maxEdge;
117 | }
118 | delaunay.Tessellate(intGeom, area);
119 | return delaunay;
120 | }
121 |
122 | public static void Tessellate(ref this Delaunay self, IntGeom intGeom, float maxArea) {
123 | var validator = new Validator(intGeom, maxArea);
124 | var unprocessed = new IndexBuffer(self.triangles.Count, Allocator.Temp);
125 |
126 | var fixIndices = new NativeArray(4, Allocator.Temp);
127 |
128 | while (unprocessed.hasNext) {
129 | int i = unprocessed.Next();
130 | var triangle = self.triangles[i];
131 |
132 | int k = validator.TestRegular(triangle);
133 |
134 | if (k < 0) {
135 | continue;
136 | }
137 |
138 | int nIx = triangle.Neighbor(k);
139 |
140 | if (nIx < 0) {
141 | continue;
142 | }
143 |
144 | var p = triangle.CircumscribedCenter();
145 |
146 | var neighbor = self.triangles[nIx];
147 |
148 | if (!neighbor.IsContain(p)) {
149 | continue;
150 | }
151 |
152 | int j = neighbor.Opposite(triangle.index);
153 | int j_next = (j + 1) % 3;
154 | int j_prev = (j + 2) % 3;
155 |
156 | if (neighbor.Neighbor(j_next) == -1 || neighbor.Neighbor(j_prev) == -1) {
157 | var njp = neighbor.Vertex(j).point;
158 | var nextCos = Validator.SqrCos(neighbor.Vertex(j_prev).point, njp, p);
159 | if (nextCos > Validator.sqrMergeCos) {
160 | continue;
161 | }
162 |
163 | var prevCos = Validator.SqrCos(njp, neighbor.Vertex(j_next).point, p);
164 | if (prevCos > Validator.sqrMergeCos) {
165 | continue;
166 | }
167 | }
168 |
169 | int k_next = (k + 1) % 3;
170 | int k_prev = (k + 2) % 3;
171 |
172 | int l = neighbor.Opposite(i);
173 |
174 | int l_next = (l + 1) % 3;
175 | int l_prev = (l + 2) % 3;
176 |
177 | var vertex = new Vertex(self.points.Count, Vertex.Nature.extraTessellated, p);
178 | self.points.Add(p);
179 |
180 | int n = self.triangles.Count;
181 |
182 | var t0 = triangle;
183 | t0.SetVertex(k_prev, vertex);
184 | t0.SetNeighbor(k_next, n);
185 | self.triangles[i] = t0;
186 | unprocessed.Add(t0.index);
187 |
188 |
189 | var t1 = neighbor;
190 | t1.SetVertex(l_next, vertex);
191 | t1.SetNeighbor(l_prev, n + 1);
192 | self.triangles[nIx] = t1;
193 | unprocessed.Add(t1.index);
194 |
195 |
196 | var t2Neighbor = triangle.Neighbor(k_next);
197 | var t2 = new Triangle(
198 | n,
199 | triangle.Vertex(k),
200 | vertex,
201 | triangle.Vertex(k_prev),
202 | n + 1,
203 | t2Neighbor,
204 | i
205 | );
206 |
207 | if (t2Neighbor >= 0) {
208 | var t2n = self.triangles[t2Neighbor];
209 | t2n.UpdateOpposite(i, n);
210 | self.triangles[t2Neighbor] = t2n;
211 | }
212 |
213 | self.triangles.Add(t2);
214 | unprocessed.Add(t2.index);
215 |
216 | var t3Neighbor = neighbor.Neighbor(l_prev);
217 | var t3 = new Triangle(
218 | n + 1,
219 | neighbor.Vertex(l),
220 | neighbor.Vertex(l_next),
221 | vertex,
222 | n,
223 | nIx,
224 | t3Neighbor
225 | );
226 |
227 | if (t3Neighbor >= 0) {
228 | var t3n = self.triangles[t3Neighbor];
229 | t3n.UpdateOpposite(nIx, n + 1);
230 | self.triangles[t3Neighbor] = t3n;
231 | }
232 |
233 | self.triangles.Add(t3);
234 | unprocessed.Add(t3.index);
235 |
236 | fixIndices[0] = i;
237 | fixIndices[1] = nIx;
238 | fixIndices[2] = n;
239 | fixIndices[3] = n + 1;
240 | self.Fix(ref unprocessed, fixIndices);
241 | }
242 |
243 | unprocessed.Dispose();
244 | fixIndices.Dispose();
245 | }
246 | }
247 |
248 |
249 | internal static class TessellationExt {
250 | internal static IntVector CircumscribedCenter(this Triangle self) {
251 | var a = self.vA.point;
252 | var b = self.vB.point;
253 | var c = self.vC.point;
254 | double ax = a.x;
255 | double ay = a.y;
256 | double bx = b.x;
257 | double by = b.y;
258 | double cx = c.x;
259 | double cy = c.y;
260 |
261 | double d = 2 * (ax * (by - cy) + bx * (cy - ay) + cx * (ay - by));
262 | double aa = ax * ax + ay * ay;
263 | double bb = bx * bx + by * by;
264 | double cc = cx * cx + cy * cy;
265 | double x = (aa * (by - cy) + bb * (cy - ay) + cc * (ay - by)) / d;
266 | double y = (aa * (cx - bx) + bb * (ax - cx) + cc * (bx - ax)) / d;
267 |
268 | return new IntVector((long) Math.Round(x, MidpointRounding.AwayFromZero), (long) Math.Round(y, MidpointRounding.AwayFromZero));
269 | }
270 |
271 | internal static bool IsContain(this Triangle self, IntVector p) {
272 | var a = self.vA.point;
273 | var b = self.vB.point;
274 | var c = self.vC.point;
275 |
276 | var d1 = Sign(p, a, b);
277 | var d2 = Sign(p, b, c);
278 | var d3 = Sign(p, c, a);
279 |
280 | bool has_neg = d1 < 0 || d2 < 0 || d3 < 0;
281 | bool has_pos = d1 > 0 || d2 > 0 || d3 > 0;
282 |
283 | return !(has_neg && has_pos);
284 | }
285 |
286 | private static long Sign(IntVector a, IntVector b, IntVector c) {
287 | return (a.x - c.x) * (b.y - c.y) - (b.x - c.x) * (a.y - c.y);
288 | }
289 | }
290 |
291 | }
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/NavigatorExt.cs:
--------------------------------------------------------------------------------
1 | using iShape.Collections;
2 | using Unity.Collections;
3 | using iShape.Geometry;
4 | using iShape.Geometry.Container;
5 | using UnityEngine;
6 |
7 | namespace iShape.Triangulation.Shape {
8 |
9 | internal static class PlainShapeNavigatorExt {
10 | private struct Node {
11 | internal readonly int index;
12 | internal IntVector point;
13 |
14 | internal Node(int index, IntVector point) {
15 | this.index = index;
16 | this.point = point;
17 | }
18 | }
19 |
20 | private struct SplitLayout {
21 | internal NativeArray layouts;
22 | internal NativeArray nodes;
23 |
24 | internal SplitLayout(NativeArray layouts, NativeArray nodes) {
25 | this.layouts = layouts;
26 | this.nodes = nodes;
27 | }
28 |
29 | internal void Dispose() {
30 | this.layouts.Dispose();
31 | this.nodes.Dispose();
32 | }
33 | }
34 |
35 | private readonly struct SortData {
36 | internal readonly int index;
37 | internal readonly long factor;
38 | private readonly int nature;
39 |
40 | internal SortData(int index, long factor, int nature) {
41 | this.index = index;
42 | this.factor = factor;
43 | this.nature = nature;
44 | }
45 |
46 | public static bool operator <(SortData a, SortData b) {
47 | if(a.factor != b.factor) {
48 | return a.factor < b.factor;
49 | } else if (a.nature != b.nature) {
50 | return a.nature < b.nature;
51 | } else {
52 | return a.index < b.index;
53 | }
54 | }
55 |
56 | public static bool operator >(SortData a, SortData b) {
57 | if(a.factor != b.factor) {
58 | return a.factor > b.factor;
59 | } else if(a.nature != b.nature) {
60 | return a.nature > b.nature;
61 | } else {
62 | return a.index > b.index;
63 | }
64 | }
65 | }
66 |
67 | internal static ShapeNavigator GetNavigator(this PlainShape shape, long maxEdge, NativeArray extraPoints, Allocator allocator) {
68 | SplitLayout splitLayout;
69 | if (maxEdge == 0) {
70 | splitLayout = shape.Plain(Allocator.Temp);
71 | } else {
72 | splitLayout = shape.Split(maxEdge, Allocator.Temp);
73 | }
74 |
75 | int pathCount = splitLayout.nodes.Length;
76 | int extraCount = extraPoints.Length;
77 |
78 | int n;
79 | if (extraCount > 0) {
80 | n = pathCount + extraCount;
81 | } else {
82 | n = pathCount;
83 | }
84 |
85 | var links = new NativeArray(n, allocator);
86 | var natures = new NativeArray(n, allocator);
87 |
88 | int m = splitLayout.layouts.Length;
89 | for(int j = 0; j < m; ++j) {
90 | var layout = splitLayout.layouts[j];
91 | var prev = layout.end - 1;
92 |
93 | var self = layout.end;
94 | var next = layout.begin;
95 |
96 | var a = splitLayout.nodes[prev];
97 | var b = splitLayout.nodes[self];
98 |
99 | var A = a.point.BitPack;
100 | var B = b.point.BitPack;
101 |
102 | while(next <= layout.end) {
103 | var c = splitLayout.nodes[next];
104 | var C = c.point.BitPack;
105 |
106 | var nature = LinkNature.simple;
107 | bool isCCW = IsCCW(a.point, b.point, c.point);
108 |
109 | if(layout.isClockWise) {
110 | if(A > B && B < C) {
111 | if(isCCW) {
112 | nature = LinkNature.start;
113 | } else {
114 | nature = LinkNature.split;
115 | }
116 | }
117 |
118 | if(A < B && B > C) {
119 | if(isCCW) {
120 | nature = LinkNature.end;
121 | } else {
122 | nature = LinkNature.merge;
123 |
124 | }
125 | }
126 | } else {
127 | if(A > B && B < C) {
128 | if(isCCW) {
129 | nature = LinkNature.start;
130 | } else {
131 | nature = LinkNature.split;
132 | }
133 | }
134 |
135 | if(A < B && B > C) {
136 | if(isCCW) {
137 | nature = LinkNature.end;
138 | } else {
139 | nature = LinkNature.merge;
140 | }
141 | }
142 | }
143 |
144 | var verNature = b.index < shape.points.Length ? Vertex.Nature.origin : Vertex.Nature.extraPath;
145 |
146 | links[self] = new Link(prev, self, next, new Vertex(self, verNature, b.point));
147 | natures[self] = nature;
148 |
149 | a = b;
150 | b = c;
151 |
152 | A = B;
153 | B = C;
154 |
155 | prev = self;
156 | self = next;
157 |
158 | ++next;
159 | }
160 | }
161 |
162 | splitLayout.Dispose();
163 |
164 | if (extraCount > 0) {
165 | for(int k = 0; k < extraPoints.Length; ++k) {
166 | var p = extraPoints[k];
167 | var j = k + pathCount;
168 | links[j] = new Link(j, j, j, new Vertex(j, Vertex.Nature.extraInner, p));
169 | natures[j] = LinkNature.extra;
170 | }
171 | }
172 |
173 | // sort
174 |
175 | var dataList = new NativeArray(n, Allocator.Temp);
176 |
177 | for(int j = 0; j < n; ++j) {
178 | var p = links[j].vertex.point;
179 | dataList[j] = new SortData(j, p.BitPack, (int)natures[j]);
180 | }
181 |
182 | Sort(dataList);
183 |
184 | var indices = new NativeArray(n, allocator);
185 |
186 | // filter same points
187 | var x1 = new SortData(-1, long.MinValue, int.MinValue);
188 |
189 | int i = 0;
190 |
191 | while(i < n) {
192 | var x0 = dataList[i];
193 | indices[i] = x0.index;
194 | if(x0.factor == x1.factor) {
195 | var v = links[x1.index].vertex;
196 |
197 | do {
198 | var link = links[x0.index];
199 | links[x0.index] = new Link(link.prev, link.self, link.next, new Vertex(v.index, v.nature, v.point));
200 | ++i;
201 | if(i < n) {
202 | x0 = dataList[i];
203 | indices[i] = x0.index;
204 | } else {
205 | break;
206 | }
207 | } while(x0.factor == x1.factor);
208 | }
209 | x1 = x0;
210 | ++i;
211 | }
212 |
213 | dataList.Dispose();
214 |
215 | return new ShapeNavigator(pathCount, extraCount, links, natures, indices);
216 | }
217 |
218 | private static void Sort(NativeArray array) {
219 | int n = array.Length;
220 | int r = 2;
221 | int rank = 1;
222 |
223 | while(r <= n) {
224 | rank = r;
225 | r <<= 1;
226 | }
227 | rank -= 1;
228 |
229 | int jEnd = rank;
230 |
231 | int jStart = ((jEnd + 1) >> 1) - 1;
232 |
233 |
234 | while(jStart >= 0) {
235 | int k = jStart;
236 | while(k < jEnd) {
237 | int j = k;
238 |
239 | var a = array[j];
240 | bool fallDown;
241 | do {
242 | fallDown = false;
243 |
244 | int j0 = (j << 1) + 1;
245 | int j1 = j0 + 1;
246 |
247 |
248 | if(j1 < n) {
249 | var a0 = array[j0];
250 | var a1 = array[j1];
251 |
252 | if(a < a0 || a < a1) {
253 | if(a0 > a1) {
254 | array[j0] = a;
255 | array[j] = a0;
256 | j = j0;
257 | } else {
258 | array[j1] = a;
259 | array[j] = a1;
260 | j = j1;
261 | }
262 | fallDown = j < rank;
263 |
264 | }
265 | } else if(j0 < n) {
266 | var ax = array[j];
267 | var a0 = array[j0];
268 | if(ax < a0) {
269 | array[j0] = ax;
270 | array[j] = a0;
271 | }
272 | }
273 |
274 | } while(fallDown);
275 | ++k;
276 | }
277 |
278 | jEnd = jStart;
279 | jStart = ((jEnd + 1) >> 1) - 1;
280 | }
281 |
282 | while(n > 0) {
283 | int m = n - 1;
284 |
285 | var a = array[m];
286 | array[m] = array[0];
287 | array[0] = a;
288 |
289 | int j = 0;
290 | bool fallDown;
291 | do {
292 | fallDown = false;
293 |
294 | int j0 = (j << 1) + 1;
295 | int j1 = j0 + 1;
296 |
297 | if(j1 < m) {
298 | var a0 = array[j0];
299 | var a1 = array[j1];
300 | fallDown = a < a0 || a < a1;
301 |
302 | if(fallDown) {
303 | if(a0 > a1) {
304 | array[j0] = a;
305 | array[j] = a0;
306 | j = j0;
307 | } else {
308 | array[j1] = a;
309 | array[j] = a1;
310 | j = j1;
311 | }
312 | }
313 | } else if(j0 < m) {
314 | var ax = array[j];
315 | var a0 = array[j0];
316 | if(ax < a0) {
317 | array[j0] = ax;
318 | array[j] = a0;
319 | }
320 | }
321 |
322 | } while(fallDown);
323 |
324 | n = m;
325 | }
326 | }
327 |
328 | private static bool IsCCW(IntVector a, IntVector b, IntVector c) {
329 | long m0 = (c.y - a.y) * (b.x - a.x);
330 | long m1 = (b.y - a.y) * (c.x - a.x);
331 |
332 | return m0 < m1;
333 | }
334 |
335 | private static SplitLayout Split(this PlainShape self, long maxEgeSize, Allocator allocator) {
336 | var originalCount = self.points.Length;
337 | var nodes = new DynamicArray(originalCount, allocator);
338 | var layouts = new DynamicArray(originalCount, allocator);
339 | var sqrMaxSize = maxEgeSize * maxEgeSize;
340 |
341 | var begin = 0;
342 | var originalIndex = 0;
343 | var extraIndex = originalCount;
344 |
345 | for (int j = 0; j < self.layouts.Length; ++j) {
346 | var layout = self.layouts[j];
347 | var last = layout.end;
348 | var a = self.points[last];
349 | var length = 0;
350 |
351 | for (int i = layout.begin; i <= layout.end; ++i) {
352 | var b = self.points[i];
353 | var dx = b.x - a.x;
354 | var dy = b.y - a.y;
355 | var sqrSize = dx * dx + dy * dy;
356 | if (sqrSize > sqrMaxSize) {
357 | var l = (long) Mathf.Sqrt(sqrSize);
358 | int s = (int) (l / maxEgeSize);
359 | double ds = s;
360 | double sx = dx / ds;
361 | double sy = dy / ds;
362 | double fx = 0;
363 | double fy = 0;
364 | for (int k = 1; k < s; ++k) {
365 | fx += sx;
366 | fy += sy;
367 |
368 | long x = a.x + (long) fx;
369 | long y = a.y + (long) fy;
370 | nodes.Add(new Node(extraIndex, new IntVector(x, y)));
371 | extraIndex += 1;
372 | }
373 |
374 | length += s - 1;
375 | }
376 |
377 | length += 1;
378 | nodes.Add(new Node(originalIndex, b));
379 | originalIndex += 1;
380 | a = b;
381 | }
382 |
383 | layouts.Add(new PathLayout(begin, length, layout.isClockWise));
384 | begin += length;
385 | }
386 |
387 | return new SplitLayout(layouts.Convert(), nodes.Convert());
388 | }
389 |
390 | private static SplitLayout Plain(this PlainShape self, Allocator allocator) {
391 | var nodes = new NativeArray(self.points.Length, allocator);
392 | for (int i = 0; i < self.points.Length; ++i) {
393 | nodes[i] = new Node(i, self.points[i]);
394 | }
395 |
396 | var layouts = new NativeArray(self.layouts, allocator);
397 |
398 | return new SplitLayout(layouts, nodes);
399 | }
400 | }
401 |
402 | }
--------------------------------------------------------------------------------
/Tests/Editor/Triangulation/MonotoneDelaunayTests.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 | using iShape.Geometry;
3 | using iShape.Geometry.Container;
4 | using iShape.Triangulation.Shape.Delaunay;
5 | using Tests.Triangulation.Util;
6 | using Tests.Triangulation.Data;
7 | using Unity.Collections;
8 | using Triangle = Tests.Triangulation.Util.Triangle;
9 |
10 |
11 | namespace Tests.Triangulation {
12 |
13 | public class MonotoneDelaunayTests {
14 |
15 | private NativeArray Triangulate(int index) {
16 | var iGeom = IntGeom.DefGeom;
17 |
18 | var data = MonotoneTests.data[index];
19 | var iPoints = iGeom.Int(data);
20 |
21 | var iShape = new IntShape(iPoints, new IntVector[0][]);
22 | var pShape = new PlainShape(iShape, Allocator.Temp);
23 |
24 | var triangles = pShape.DelaunayTriangulate(Allocator.Temp);
25 |
26 | Assert.IsTrue(Triangle.IsCCW(pShape.points, triangles));
27 |
28 | pShape.Dispose();
29 |
30 | return triangles;
31 | }
32 |
33 | [Test]
34 | public void TestTriangulate_00() {
35 | var triangles = this.Triangulate(0);
36 | var origin = new NativeArray(new int[] {
37 | 0, 1, 3,
38 | 1, 2, 3
39 | }, Allocator.Temp);
40 |
41 | bool isEqual = triangles.CompareTriangles(origin);
42 | Assert.IsTrue(isEqual);
43 | triangles.Dispose();
44 | origin.Dispose();
45 | }
46 |
47 | [Test]
48 | public void TestTriangulate_01() {
49 | var triangles = this.Triangulate(1);
50 | var origin = new NativeArray(new int[] {
51 | 0, 1, 3,
52 | 3, 1, 2
53 | }, Allocator.Temp);
54 |
55 | bool isEqual = triangles.CompareTriangles(origin);
56 | Assert.IsTrue(isEqual);
57 | triangles.Dispose();
58 | origin.Dispose();
59 | }
60 |
61 | [Test]
62 | public void TestTriangulate_02() {
63 | var triangles = this.Triangulate(2);
64 | var origin = new NativeArray(new int[] {
65 | 1, 2, 0,
66 | 0, 2, 4,
67 | 2, 3, 4
68 | }, Allocator.Temp);
69 |
70 | bool isEqual = triangles.CompareTriangles(origin);
71 | Assert.IsTrue(isEqual);
72 | triangles.Dispose();
73 | origin.Dispose();
74 | }
75 |
76 | [Test]
77 | public void TestTriangulate_03() {
78 | var triangles = this.Triangulate(3);
79 | var origin = new NativeArray(new int[] {
80 | 3, 1, 2,
81 | 1, 4, 0,
82 | 3, 4, 1
83 | }, Allocator.Temp);
84 |
85 | bool isEqual = triangles.CompareTriangles(origin);
86 | Assert.IsTrue(isEqual);
87 | triangles.Dispose();
88 | origin.Dispose();
89 | }
90 |
91 | [Test]
92 | public void TestTriangulate_04() {
93 | var triangles = this.Triangulate(4);
94 | var origin = new NativeArray(new int[] {
95 | 3, 0, 2,
96 | 0, 1, 2
97 | }, Allocator.Temp);
98 |
99 | bool isEqual = triangles.CompareTriangles(origin);
100 | Assert.IsTrue(isEqual);
101 | triangles.Dispose();
102 | origin.Dispose();
103 | }
104 |
105 | [Test]
106 | public void TestTriangulate_05() {
107 | var triangles = this.Triangulate(5);
108 | var origin = new NativeArray(new int[] {
109 | 0, 1, 2,
110 | 0, 2, 4,
111 | 2, 3, 4
112 | }, Allocator.Temp);
113 |
114 | bool isEqual = triangles.CompareTriangles(origin);
115 | Assert.IsTrue(isEqual);
116 | triangles.Dispose();
117 | origin.Dispose();
118 | }
119 |
120 | [Test]
121 | public void TestTriangulate_06() {
122 | var triangles = this.Triangulate(6);
123 | var origin = new NativeArray(new int[] {
124 | 1, 2, 3,
125 | 1, 3, 0,
126 | 4, 0, 3
127 | }, Allocator.Temp);
128 |
129 | bool isEqual = triangles.CompareTriangles(origin);
130 | Assert.IsTrue(isEqual);
131 | triangles.Dispose();
132 | origin.Dispose();
133 | }
134 |
135 | [Test]
136 | public void TestTriangulate_07() {
137 | var triangles = this.Triangulate(7);
138 | var origin = new NativeArray(new int[] {
139 | 0, 1, 8,
140 | 8, 1, 6,
141 | 8, 6, 7,
142 | 2, 6, 1,
143 | 3, 6, 2,
144 | 5, 6, 3,
145 | 4, 5, 3
146 | }, Allocator.Temp);
147 |
148 | bool isEqual = triangles.CompareTriangles(origin);
149 | Assert.IsTrue(isEqual);
150 | triangles.Dispose();
151 | origin.Dispose();
152 | }
153 |
154 | [Test]
155 | public void TestTriangulate_08() {
156 | var triangles = this.Triangulate(8);
157 | var origin = new NativeArray(new int[] {
158 | 6, 0, 1,
159 | 8, 0, 6,
160 | 8, 6, 7,
161 | 2, 6, 1,
162 | 3, 6, 2,
163 | 5, 6, 3,
164 | 4, 5, 3
165 | }, Allocator.Temp);
166 |
167 | bool isEqual = triangles.CompareTriangles(origin);
168 | Assert.IsTrue(isEqual);
169 | triangles.Dispose();
170 | origin.Dispose();
171 | }
172 |
173 | [Test]
174 | public void TestTriangulate_09() {
175 | var triangles = this.Triangulate(9);
176 | var origin = new NativeArray(new int[] {
177 | 0, 1, 8,
178 | 7, 8, 1,
179 | 6, 7, 1,
180 | 1, 2, 3,
181 | 1, 3, 6,
182 | 5, 6, 3,
183 | 4, 5, 3
184 | }, Allocator.Temp);
185 |
186 | bool isEqual = triangles.CompareTriangles(origin);
187 | Assert.IsTrue(isEqual);
188 | triangles.Dispose();
189 | origin.Dispose();
190 | }
191 |
192 | [Test]
193 | public void TestTriangulate_10() {
194 | var triangles = this.Triangulate(10);
195 | var origin = new NativeArray(new int[] {
196 | 8, 0, 1,
197 | 8, 1, 7,
198 | 1, 6, 7,
199 | 1, 2, 6,
200 | 6, 2, 3,
201 | 6, 3, 5,
202 | 3, 4, 5
203 | }, Allocator.Temp);
204 |
205 | bool isEqual = triangles.CompareTriangles(origin);
206 | Assert.IsTrue(isEqual);
207 | triangles.Dispose();
208 | origin.Dispose();
209 | }
210 |
211 | [Test]
212 | public void TestTriangulate_11() {
213 | var triangles = this.Triangulate(11);
214 | var origin = new NativeArray(new int[] {
215 | 8, 0, 6,
216 | 8, 6, 7,
217 | 1, 6, 0,
218 | 1, 2, 3,
219 | 1, 3, 6,
220 | 5, 6, 3,
221 | 4, 5, 3
222 | }, Allocator.Temp);
223 |
224 | bool isEqual = triangles.CompareTriangles(origin);
225 | Assert.IsTrue(isEqual);
226 | triangles.Dispose();
227 | origin.Dispose();
228 | }
229 |
230 | [Test]
231 | public void TestTriangulate_12() {
232 | var triangles = this.Triangulate(12);
233 | var origin = new NativeArray(new int[] {
234 | 14, 0, 1,
235 | 14, 1, 13,
236 | 1, 2, 3,
237 | 13, 1, 10,
238 | 10, 12, 13,
239 | 10, 11, 12,
240 | 1, 3, 4,
241 | 1, 4, 10,
242 | 9, 10, 4,
243 | 5, 9, 4,
244 | 5, 6, 8,
245 | 5, 8, 9,
246 | 7, 8, 6
247 | }, Allocator.Temp);
248 |
249 | bool isEqual = triangles.CompareTriangles(origin);
250 | Assert.IsTrue(isEqual);
251 | triangles.Dispose();
252 | origin.Dispose();
253 | }
254 |
255 | [Test]
256 | public void TestTriangulate_13() {
257 | var triangles = this.Triangulate(13);
258 | var origin = new NativeArray(new int[] {
259 | 14, 0, 1,
260 | 14, 1, 13,
261 | 2, 3, 4,
262 | 10, 13, 1,
263 | 12, 13, 10,
264 | 12, 10, 11,
265 | 2, 4, 1,
266 | 1, 4, 10,
267 | 9, 10, 4,
268 | 5, 9, 4,
269 | 5, 6, 8,
270 | 5, 8, 9,
271 | 7, 8, 6
272 | }, Allocator.Temp);
273 |
274 | bool isEqual = triangles.CompareTriangles(origin);
275 | Assert.IsTrue(isEqual);
276 | triangles.Dispose();
277 | origin.Dispose();
278 | }
279 |
280 | [Test]
281 | public void TestTriangulate_14() {
282 | var triangles = this.Triangulate(14);
283 | var origin = new NativeArray(new int[] {
284 | 1, 2, 8,
285 | 2, 3, 7,
286 | 6, 3, 4,
287 | 9, 0, 1,
288 | 1, 8, 9,
289 | 2, 7, 8,
290 | 6, 7, 3,
291 | 5, 6, 4
292 | }, Allocator.Temp);
293 |
294 | bool isEqual = triangles.CompareTriangles(origin);
295 | Assert.IsTrue(isEqual);
296 | triangles.Dispose();
297 | origin.Dispose();
298 | }
299 |
300 | [Test]
301 | public void TestTriangulate_15() {
302 | var triangles = this.Triangulate(15);
303 | var origin = new NativeArray(new int[] {
304 | 0, 1, 3,
305 | 3, 1, 2,
306 | 13, 0, 3,
307 | 13, 15, 0,
308 | 13, 14, 15,
309 | 5, 3, 4,
310 | 5, 13, 3,
311 | 11, 13, 5,
312 | 11, 12, 13,
313 | 7, 5, 6,
314 | 8, 5, 7,
315 | 8, 11, 5,
316 | 9, 10, 11,
317 | 11, 8, 9
318 | }, Allocator.Temp);
319 |
320 | bool isEqual = triangles.CompareTriangles(origin);
321 | Assert.IsTrue(isEqual);
322 | triangles.Dispose();
323 | origin.Dispose();
324 | }
325 |
326 | [Test]
327 | public void TestTriangulate_16() {
328 | var triangles = this.Triangulate(16);
329 | var origin = new NativeArray(new int[] {
330 | 12, 0, 1,
331 | 12, 1, 11,
332 | 1, 2, 4,
333 | 5, 11, 1,
334 | 4, 2, 3,
335 | 5, 1, 4,
336 | 5, 10, 11,
337 | 10, 5, 8,
338 | 10, 8, 9,
339 | 5, 6, 7,
340 | 5, 7, 8
341 | }, Allocator.Temp);
342 |
343 | bool isEqual = triangles.CompareTriangles(origin);
344 | Assert.IsTrue(isEqual);
345 | triangles.Dispose();
346 | origin.Dispose();
347 | }
348 |
349 | [Test]
350 | public void TestTriangulate_17() {
351 | var triangles = this.Triangulate(17);
352 | var origin = new NativeArray(new int[] {
353 | 1, 13, 0,
354 | 12, 13, 1,
355 | 2, 12, 1,
356 | 3, 12, 2,
357 | 10, 11, 12,
358 | 9, 10, 12,
359 | 12, 3, 9,
360 | 4, 9, 3,
361 | 5, 9, 4,
362 | 8, 9, 5,
363 | 5, 6, 7,
364 | 5, 7, 8
365 | }, Allocator.Temp);
366 |
367 | bool isEqual = triangles.CompareTriangles(origin);
368 | Assert.IsTrue(isEqual);
369 | triangles.Dispose();
370 | origin.Dispose();
371 | }
372 | }
373 | }
--------------------------------------------------------------------------------
/Tests/Editor/Triangulation/MonotonePlainTests.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 | using iShape.Geometry;
3 | using iShape.Triangulation.Shape;
4 | using Tests.Triangulation.Util;
5 | using Tests.Triangulation.Data;
6 | using Unity.Collections;
7 | using iShape.Geometry.Container;
8 | using Triangle = Tests.Triangulation.Util.Triangle;
9 |
10 |
11 | namespace Tests.Triangulation {
12 |
13 | public class MonotonePlainTests {
14 |
15 | private NativeArray Triangulate(int index) {
16 | var iGeom = IntGeom.DefGeom;
17 |
18 | var data = MonotoneTests.data[index];
19 | var iPoints = iGeom.Int(data);
20 |
21 | var iShape = new IntShape(iPoints, new IntVector[0][]);
22 | var pShape = new PlainShape(iShape, Allocator.Temp);
23 |
24 | var triangles = pShape.Triangulate(Allocator.Temp);
25 |
26 | Assert.IsTrue(Triangle.IsCCW(pShape.points, triangles));
27 |
28 | pShape.Dispose();
29 |
30 | return triangles;
31 | }
32 |
33 | [Test]
34 | public void TestTriangulate_00() {
35 | var triangles = this.Triangulate(0);
36 | var origin = new NativeArray(new int[] {
37 | 0, 1, 3,
38 | 1, 2, 3
39 | }, Allocator.Temp);
40 |
41 | bool isEqual = triangles.CompareTriangles(origin);
42 | Assert.IsTrue(isEqual);
43 | triangles.Dispose();
44 | origin.Dispose();
45 | }
46 |
47 | [Test]
48 | public void TestTriangulate_01() {
49 | var triangles = this.Triangulate(1);
50 | var origin = new NativeArray(new int[] {
51 | 0, 1, 3,
52 | 3, 1, 2
53 | }, Allocator.Temp);
54 |
55 |
56 | bool isEqual = triangles.CompareTriangles(origin);
57 | Assert.IsTrue(isEqual);
58 | triangles.Dispose();
59 | origin.Dispose();
60 | }
61 |
62 | [Test]
63 | public void TestTriangulate_02() {
64 | var triangles = this.Triangulate(2);
65 | var origin = new NativeArray(new int[] {
66 | 1, 2, 0,
67 | 0, 2, 4,
68 | 2, 3, 4
69 | }, Allocator.Temp);
70 | bool isEqual = triangles.CompareTriangles(origin);
71 | Assert.IsTrue(isEqual);
72 | triangles.Dispose();
73 | origin.Dispose();
74 | }
75 |
76 | [Test]
77 | public void TestTriangulate_03() {
78 | var triangles = this.Triangulate(3);
79 | var origin = new NativeArray(new int[] {
80 | 1, 2, 0,
81 | 0, 2, 4,
82 | 2, 3, 4
83 | }, Allocator.Temp);
84 | bool isEqual = triangles.CompareTriangles(origin);
85 | Assert.IsTrue(isEqual);
86 | triangles.Dispose();
87 | origin.Dispose();
88 | }
89 |
90 | [Test]
91 | public void TestTriangulate_04() {
92 | var triangles = this.Triangulate(4);
93 | var origin = new NativeArray(new int[] {
94 | 3, 0, 2,
95 | 0, 1, 2
96 | }, Allocator.Temp);
97 | bool isEqual = triangles.CompareTriangles(origin);
98 | Assert.IsTrue(isEqual);
99 | triangles.Dispose();
100 | origin.Dispose();
101 | }
102 |
103 | [Test]
104 | public void TestTriangulate_05() {
105 | var triangles = this.Triangulate(5);
106 | var origin = new NativeArray(new int[] {
107 | 0, 1, 2,
108 | 0, 2, 4,
109 | 2, 3, 4
110 | }, Allocator.Temp);
111 | bool isEqual = triangles.CompareTriangles(origin);
112 | Assert.IsTrue(isEqual);
113 | triangles.Dispose();
114 | origin.Dispose();
115 |
116 |
117 | }
118 |
119 | [Test]
120 | public void TestTriangulate_06() {
121 | var triangles = this.Triangulate(6);
122 | var origin = new NativeArray(new int[] {
123 | 0, 1, 2,
124 | 0, 2, 3,
125 | 0, 3, 4
126 | }, Allocator.Temp);
127 | bool isEqual = triangles.CompareTriangles(origin);
128 | Assert.IsTrue(isEqual);
129 | triangles.Dispose();
130 | origin.Dispose();
131 | }
132 |
133 | [Test]
134 | public void TestTriangulate_07() {
135 | var triangles = this.Triangulate(7);
136 | var origin = new NativeArray(new int[] {
137 | 8, 0, 1,
138 | 8, 1, 7,
139 | 1, 6, 7,
140 | 1, 2, 6,
141 | 6, 2, 3,
142 | 6, 3, 5,
143 | 3, 4, 5
144 | }, Allocator.Temp);
145 | bool isEqual = triangles.CompareTriangles(origin);
146 | Assert.IsTrue(isEqual);
147 | triangles.Dispose();
148 | origin.Dispose();
149 | }
150 |
151 | [Test]
152 | public void TestTriangulate_08() {
153 | var triangles = this.Triangulate(8);
154 | var origin = new NativeArray(new int[] {
155 | 8, 0, 1,
156 | 8, 1, 7,
157 | 1, 6, 7,
158 | 1, 2, 6,
159 | 6, 2, 3,
160 | 6, 3, 5,
161 | 3, 4, 5
162 | }, Allocator.Temp);
163 | bool isEqual = triangles.CompareTriangles(origin);
164 | Assert.IsTrue(isEqual);
165 | triangles.Dispose();
166 | origin.Dispose();
167 | }
168 |
169 | [Test]
170 | public void TestTriangulate_09() {
171 | var triangles = this.Triangulate(9);
172 | var origin = new NativeArray(new int[] {
173 | 8, 0, 1,
174 | 8, 1, 7,
175 | 1, 6, 7,
176 | 1, 2, 6,
177 | 6, 2, 3,
178 | 6, 3, 5,
179 | 3, 4, 5
180 | }, Allocator.Temp);
181 | bool isEqual = triangles.CompareTriangles(origin);
182 | Assert.IsTrue(isEqual);
183 | triangles.Dispose();
184 | origin.Dispose();
185 | }
186 |
187 | [Test]
188 | public void TestTriangulate_10() {
189 | var triangles = this.Triangulate(10);
190 | var origin = new NativeArray(new int[] {
191 | 8, 0, 1,
192 | 8, 1, 7,
193 | 1, 6, 7,
194 | 1, 2, 6,
195 | 6, 2, 3,
196 | 6, 3, 5,
197 | 3, 4, 5
198 | }, Allocator.Temp);
199 | bool isEqual = triangles.CompareTriangles(origin);
200 | Assert.IsTrue(isEqual);
201 | triangles.Dispose();
202 | origin.Dispose();
203 | }
204 |
205 | [Test]
206 | public void TestTriangulate_11() {
207 | var triangles = this.Triangulate(11);
208 | var origin = new NativeArray(new int[] {
209 | 8, 0, 7,
210 | 0, 6, 7,
211 | 0, 1, 6,
212 | 6, 2, 3,
213 | 6, 3, 5,
214 | 3, 4, 5
215 | }, Allocator.Temp);
216 | bool isEqual = triangles.CompareTriangles(origin);
217 | Assert.IsTrue(isEqual);
218 | triangles.Dispose();
219 | origin.Dispose();
220 | }
221 |
222 |
223 | [Test]
224 | public void TestTriangulate_12() {
225 | var triangles = this.Triangulate(12);
226 | var origin = new NativeArray(new int[] {
227 | 0, 13, 14,
228 | 0, 1, 13,
229 | 13, 1, 2,
230 | 13, 2, 12,
231 | 2, 11, 12,
232 | 2, 10, 11,
233 | 2, 3, 10,
234 | 10, 3, 4,
235 | 10, 4, 9,
236 | 4, 5, 9,
237 | 9, 5, 6,
238 | 9, 6, 8,
239 | 6, 7, 8
240 | }, Allocator.Temp);
241 | bool isEqual = triangles.CompareTriangles(origin);
242 | Assert.IsTrue(isEqual);
243 | triangles.Dispose();
244 | origin.Dispose();
245 | }
246 |
247 |
248 | [Test]
249 | public void TestTriangulate_13() {
250 | var triangles = this.Triangulate(13);
251 | var origin = new NativeArray(new int[] {
252 | 0, 13, 14,
253 | 0, 1, 13,
254 | 13, 1, 12,
255 | 1, 2, 12,
256 | 12, 2, 11,
257 | 2, 10, 11,
258 | 2, 3, 10,
259 | 10, 3, 4,
260 | 10, 4, 9,
261 | 4, 5, 9,
262 | 9, 5, 6,
263 | 9, 6, 8,
264 | 6, 7, 8
265 | }, Allocator.Temp);
266 | bool isEqual = triangles.CompareTriangles(origin);
267 | Assert.IsTrue(isEqual);
268 | triangles.Dispose();
269 | origin.Dispose();
270 | }
271 |
272 |
273 | [Test]
274 | public void TestTriangulate_14() {
275 | var triangles = this.Triangulate(14);
276 | var origin = new NativeArray(new int[] {
277 | 0, 4, 9,
278 | 4, 8, 9,
279 | 4, 7, 8,
280 | 4, 6, 7,
281 | 4, 5, 6
282 | }, Allocator.Temp);
283 | bool isEqual = triangles.CompareTriangles(origin);
284 | Assert.IsTrue(isEqual);
285 | triangles.Dispose();
286 | origin.Dispose();
287 | }
288 |
289 |
290 | [Test]
291 | public void TestTriangulate_15() {
292 | var triangles = this.Triangulate(15);
293 | var origin = new NativeArray(new int[] {
294 | 0, 1, 15,
295 | 1, 2, 15,
296 | 15, 2, 14,
297 | 2, 3, 14,
298 | 14, 3, 13,
299 | 3, 4, 13,
300 | 13, 4, 12,
301 | 4, 5, 12,
302 | 12, 5, 11,
303 | 5, 6, 11,
304 | 11, 6, 10,
305 | 6, 7, 10,
306 | 10, 7, 9,
307 | 7, 8, 9
308 | }, Allocator.Temp);
309 | bool isEqual = triangles.CompareTriangles(origin);
310 | Assert.IsTrue(isEqual);
311 | triangles.Dispose();
312 | origin.Dispose();
313 | }
314 |
315 | [Test]
316 | public void TestTriangulate_16() {
317 | var triangles = this.Triangulate(16);
318 | var origin = new NativeArray(new int[] {
319 | 0, 11, 12,
320 | 0, 1, 11,
321 | 11, 1, 2,
322 | 11, 2, 10,
323 | 2, 3, 10,
324 | 10, 3, 9,
325 | 3, 4, 9,
326 | 9, 4, 5,
327 | 9, 5, 8,
328 | 5, 6, 8,
329 | 8, 6, 7
330 | }, Allocator.Temp);
331 | bool isEqual = triangles.CompareTriangles(origin);
332 | Assert.IsTrue(isEqual);
333 | triangles.Dispose();
334 | origin.Dispose();
335 | }
336 |
337 | [Test]
338 | public void TestTriangulate_17() {
339 | var triangles = this.Triangulate(17);
340 | var origin = new NativeArray(new int[] {
341 | 0, 1, 13,
342 | 13, 1, 12,
343 | 1, 2, 12,
344 | 12, 10, 11,
345 | 2, 10, 12,
346 | 2, 9, 10,
347 | 2, 3, 9,
348 | 9, 3, 4,
349 | 9, 4, 5,
350 | 9, 5, 8,
351 | 5, 6, 8,
352 | 8, 6, 7
353 | }, Allocator.Temp);
354 | bool isEqual = triangles.CompareTriangles(origin);
355 | Assert.IsTrue(isEqual);
356 | triangles.Dispose();
357 | origin.Dispose();
358 | }
359 | }
360 | }
361 |
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/Delaunay/CentroidNet.cs:
--------------------------------------------------------------------------------
1 | using iShape.Collections;
2 | using iShape.Geometry;
3 | using iShape.Geometry.Container;
4 | using iShape.Geometry.Extension;
5 | using iShape.Geometry.Polygon;
6 | using Unity.Collections;
7 | using UnityEngine;
8 |
9 | namespace iShape.Triangulation.Shape.Delaunay {
10 |
11 | public static class CentroidNet {
12 |
13 | public static List MakeCentroidNet(this PlainShape self, Allocator allocator, IntGeom intGeom, float maxEdge, float maxArea = 0, float minArea = 0, bool onlyConvex = false) {
14 | long iEdge = intGeom.Int(maxEdge);
15 | var delaunay = self.Delaunay(iEdge, Allocator.Temp);
16 | float aMaxArea;
17 | if (maxArea > 0) {
18 | aMaxArea = maxArea;
19 | } else {
20 | aMaxArea = 0.4f * maxEdge * maxEdge;
21 | }
22 |
23 | delaunay.Tessellate(intGeom, aMaxArea);
24 |
25 | var iMinArea = intGeom.SqrInt(minArea);
26 | var shape = delaunay.MakeCentroidNet(Allocator.Temp, iMinArea, onlyConvex);
27 | delaunay.Dispose();
28 |
29 | int n = shape.layouts.Length;
30 | var dynamicList = new DynamicList(8 * n, n, allocator);
31 |
32 | for (int i = 0; i < n; ++i) {
33 | var iPath = shape.Get(i);
34 | var path = intGeom.Float(iPath, Allocator.Temp);
35 | var polygon = new Polygon(path, Allocator.Temp);
36 | dynamicList.Add(polygon);
37 | }
38 |
39 | shape.Dispose();
40 |
41 | return dynamicList.Convert();
42 | }
43 |
44 | public static PlainShape MakeCentroidNet(this Delaunay self, Allocator allocator, long minArea = 0, bool onlyConvex = false) {
45 | int n = self.triangles.Count;
46 |
47 | var details = new NativeArray(n, Allocator.Temp);
48 | for (int i = 0; i < n; ++i) {
49 | var triangle = self.triangles[i];
50 | var count = 0;
51 | if (triangle.nA >= 0) {
52 | ++count;
53 | }
54 |
55 | if (triangle.nB >= 0) {
56 | ++count;
57 | }
58 |
59 | if (triangle.nC >= 0) {
60 | ++count;
61 | }
62 |
63 | details[i] = new Detail(triangle.Center(), count);
64 | }
65 |
66 | int capacity = self.points.Count;
67 |
68 | var visitedIndex = new NativeArray(capacity, Allocator.Temp);
69 | var result = new DynamicPlainShape(8 * capacity, capacity, allocator);
70 | var forePoints = new NativeArray(4, Allocator.Temp);
71 | var path = new DynamicArray(8, Allocator.Temp);
72 | var subPath = new DynamicArray(8, Allocator.Temp);
73 |
74 | for (int i = 0; i < n; ++i) {
75 | var triangle = self.triangles[i];
76 | var detail = details[i];
77 |
78 | for (int j = 0; j <= 2; ++j) {
79 |
80 | var v = triangle.Vertex(j);
81 | if (visitedIndex[v.index]) {
82 | continue;
83 | }
84 |
85 | visitedIndex[v.index] = true;
86 |
87 | if (v.isPath) {
88 | if (detail.count == 1 && triangle.Neighbor(j) >= 0) {
89 | switch (j) {
90 | case 0: // a
91 | var ab0 = v.point.Center(triangle.vB.point);
92 | var ca0 = v.point.Center(triangle.vC.point);
93 |
94 | forePoints[0] = v.point;
95 | forePoints[1] = ab0;
96 | forePoints[2] = detail.center;
97 | forePoints[3] = ca0;
98 | break;
99 | case 1: // b
100 | var bc1 = v.point.Center(point: triangle.vC.point);
101 | var ab1 = v.point.Center(point: triangle.vA.point);
102 |
103 | forePoints[0] = v.point;
104 | forePoints[1] = bc1;
105 | forePoints[2] = detail.center;
106 | forePoints[3] = ab1;
107 | break;
108 | default: // c
109 | var ca2 = v.point.Center(point: triangle.vA.point);
110 | var bc2 = v.point.Center(point: triangle.vB.point);
111 |
112 | forePoints[0] = v.point;
113 | forePoints[1] = ca2;
114 | forePoints[2] = detail.center;
115 | forePoints[3] = bc2;
116 | break;
117 | }
118 |
119 | if (minArea == 0 || forePoints.Area() > minArea) {
120 | result.Add(forePoints, true);
121 | }
122 | } else {
123 | path.RemoveAll();
124 | // first going in a counterclockwise direction
125 | var current = triangle;
126 | int k = triangle.FindIndex(v.index);
127 | int right = (k + 2) % 3;
128 | var prev = triangle.Neighbor(right);
129 | while (prev >= 0) {
130 | var prevTriangle = self.triangles[prev];
131 | k = prevTriangle.FindIndex(v.index);
132 | if (k < 0) {
133 | break;
134 | }
135 |
136 | current = prevTriangle;
137 | path.Add(details[prev].center);
138 |
139 | right = (k + 2) % 3;
140 | prev = current.Neighbor(right);
141 | }
142 |
143 | var left = (k + 1) % 3;
144 | var lastPrevPair = current.Vertex(left).point;
145 | path.Add(lastPrevPair.Center(v.point));
146 |
147 | path.Reverse();
148 |
149 | path.Add(details[i].center);
150 |
151 | // now going in a clockwise direction
152 | current = triangle;
153 | k = triangle.FindIndex(v.index);
154 | left = (k + 1) % 3;
155 | var next = triangle.Neighbor(left);
156 | while (next >= 0) {
157 | var nextTriangle = self.triangles[next];
158 | k = nextTriangle.FindIndex(v.index);
159 | if (k < 0) {
160 | break;
161 | }
162 |
163 | current = nextTriangle;
164 | path.Add(details[next].center);
165 | left = (k + 1) % 3;
166 | next = current.Neighbor(left);
167 | }
168 |
169 | right = (k + 2) % 3;
170 | var lastNextPair = current.Vertex(right).point;
171 | path.Add(lastNextPair.Center(v.point));
172 |
173 | if (onlyConvex) {
174 | // split path into convex subPath
175 | var c = v.point;
176 | var p0 = path[0];
177 | var v0 = p0 - c;
178 | var d0 = v0;
179 |
180 | subPath.RemoveAll();
181 |
182 | subPath.Add(c);
183 | subPath.Add(path[0]);
184 | for (int t = 1; t < path.Count; ++t) {
185 |
186 | var p1 = path[t];
187 | var d1 = p1 - p0;
188 | var v1 = p1 - c;
189 | if (v0.CrossProduct(v1) <= 0 && d0.CrossProduct(d1) <= 0) {
190 | subPath.Add(p1);
191 | } else {
192 | if (minArea == 0 || subPath.slice.Area() > minArea) {
193 | result.Add(subPath.slice, true);
194 | }
195 | subPath.RemoveAll();
196 | subPath.Add(c);
197 | subPath.Add(p0);
198 | subPath.Add(p1);
199 | v0 = p0 - c;
200 | }
201 |
202 | p0 = p1;
203 | d0 = d1;
204 | }
205 |
206 | if (minArea == 0 || subPath.slice.Area() > minArea) {
207 | result.Add(subPath.slice, true);
208 | }
209 | } else {
210 | path.Add(v.point);
211 | if (minArea == 0 || path.slice.Area() > minArea) {
212 | result.Add(path.slice, true);
213 | }
214 | }
215 | }
216 | } else {
217 | path.RemoveAll();
218 | int start = i;
219 | var next = start;
220 | do {
221 | var t = self.triangles[next];
222 | var center = details[next].center;
223 | path.Add(center);
224 | int index = (t.FindIndex(v.index) + 1) % 3;
225 | next = t.Neighbor(index);
226 | } while (next != start && next >= 0);
227 | if (minArea == 0 || path.slice.Area() > minArea) {
228 | result.Add(path.slice, true);
229 | }
230 | }
231 | }
232 | }
233 |
234 | path.Dispose();
235 | subPath.Dispose();
236 | forePoints.Dispose();
237 | details.Dispose();
238 | visitedIndex.Dispose();
239 |
240 | return result.Convert();
241 | }
242 | private readonly struct Detail {
243 | internal readonly IntVector center;
244 | internal readonly int count;
245 |
246 | internal Detail(IntVector center, int count) {
247 | this.center = center;
248 | this.count = count;
249 | }
250 | }
251 | private static IntVector Center(this Triangle self) {
252 | var a = self.vA.point;
253 | var b = self.vB.point;
254 | var c = self.vC.point;
255 |
256 | return new IntVector((a.x + b.x + c.x) / 3, (a.y + b.y + c.y) / 3);
257 | }
258 |
259 | private static IntVector Center(this IntVector self, IntVector point) {
260 | return new IntVector((self.x + point.x) / 2, (self.y + point.y) / 2);
261 | }
262 |
263 | private static void Reverse(this NativeList self) {
264 | int length = self.Length;
265 | int n = self.Length >> 1;
266 | for (int i = 0, j = length - 1; i < n; ++i, --j) {
267 | var a = self[i];
268 | var b = self[j];
269 | self[j] = a;
270 | self[i] = b;
271 | }
272 | }
273 |
274 | }
275 | }
--------------------------------------------------------------------------------
/Readme/star_polygon.svg:
--------------------------------------------------------------------------------
1 |
2 |
216 |
--------------------------------------------------------------------------------
/Runtime/iShape/Triangulation/Shape/Delaunay/Delaunay.cs:
--------------------------------------------------------------------------------
1 | using Unity.Collections;
2 | using iShape.Geometry;
3 | using iShape.Collections;
4 | using UnityEngine;
5 |
6 | namespace iShape.Triangulation.Shape.Delaunay {
7 |
8 | public struct Delaunay {
9 |
10 | internal DynamicArray points;
11 | internal DynamicArray triangles;
12 |
13 | public NativeArray Indices(Allocator allocator) {
14 | int n = triangles.Count;
15 | var result = new NativeArray(3 * n, allocator);
16 | int i = 0;
17 | int j = 0;
18 | do {
19 | var triangle = this.triangles[i];
20 | result[j] = triangle.vA.index;
21 | result[j + 1] = triangle.vB.index;
22 | result[j + 2] = triangle.vC.index;
23 |
24 | j += 3;
25 | i += 1;
26 | } while(i < n);
27 |
28 | return result;
29 | }
30 |
31 | public NativeArray Vertices(Allocator allocator, IntGeom intGeom, float z = 0) {
32 | int n = points.Count;
33 | var result = new NativeArray(n, allocator);
34 | for (int i = 0; i < n; ++i) {
35 | var p = intGeom.Float(points[i]);
36 | result[i] = new Vector3(p.x, p.y, z);
37 | }
38 |
39 | return result;
40 | }
41 |
42 | public Delaunay(NativeArray points, NativeArray triangles, Allocator allocator) {
43 | this.points = new DynamicArray(points, allocator);
44 | this.triangles = new DynamicArray(triangles, allocator);
45 | }
46 |
47 | public void Build() {
48 | int count = triangles.Count;
49 | var visitMarks = new NativeArray(count, Allocator.Temp);
50 | var visitIndex = 0;
51 |
52 | var origin = new DynamicArray(16, Allocator.Temp);
53 | var buffer = new DynamicArray(16, Allocator.Temp);
54 |
55 | origin.Add(0);
56 |
57 | while(origin.Count > 0) {
58 | buffer.RemoveAll();
59 | for(int l = 0; l < origin.Count; ++l) {
60 | int i = origin[l];
61 | var triangle = this.triangles[i];
62 | visitMarks[i] = true;
63 |
64 | for(int k = 0; k < 3; ++k) {
65 |
66 | int neighborIndex = triangle.Neighbor(k);
67 | if(neighborIndex >= 0) {
68 | var neighbor = triangles[neighborIndex];
69 | if(this.Swap(triangle, neighbor)) {
70 |
71 | triangle = this.triangles[triangle.index];
72 | neighbor = this.triangles[neighbor.index];
73 |
74 | for(int j = 0; j < 3; ++j) {
75 | int ni = triangle.Neighbor(j);
76 | if(ni >= 0 && ni != neighbor.index) {
77 | buffer.Add(ni);
78 | }
79 | }
80 |
81 | for(int j = 0; j < 3; ++j) {
82 | int ni = neighbor.Neighbor(j);
83 | if(ni >= 0 && ni != triangle.index) {
84 | buffer.Add(ni);
85 | }
86 | }
87 | }
88 | }
89 | }
90 | }
91 | origin.RemoveAll();
92 |
93 | if(buffer.Count == 0 && visitIndex < count) {
94 | ++visitIndex;
95 | while(visitIndex < count) {
96 | if(visitMarks[visitIndex] == false) {
97 | origin.Add(visitIndex);
98 | break;
99 | }
100 | ++visitIndex;
101 | }
102 | } else {
103 | origin.Add(buffer);
104 | }
105 | }
106 |
107 | origin.Dispose();
108 | buffer.Dispose();
109 | visitMarks.Dispose();
110 | }
111 | public void Dispose() {
112 | this.points.Dispose();
113 | this.triangles.Dispose();
114 | }
115 |
116 | internal static bool IsDelaunay(IntVector p0, IntVector p1, IntVector p2, IntVector p3) {
117 | long x01 = p0.x - p1.x;
118 | long x03 = p0.x - p3.x;
119 | long x12 = p1.x - p2.x;
120 | long x32 = p3.x - p2.x;
121 |
122 | long y01 = p0.y - p1.y;
123 | long y03 = p0.y - p3.y;
124 | long y12 = p1.y - p2.y;
125 | long y23 = p2.y - p3.y;
126 |
127 | long cosA = x01 * x03 + y01 * y03;
128 | long cosB = x12 * x32 - y12 * y23;
129 |
130 | if (cosA < 0 && cosB < 0) {
131 | return false;
132 | }
133 |
134 | if (cosA >= 0 && cosB >= 0) {
135 | return true;
136 | }
137 |
138 | // we can not just compare
139 | // sinA * cosB + cosA * sinB ? 0
140 | // cause we need weak Delaunay condition
141 |
142 | long sinA = x01 * y03 - x03 * y01;
143 | long sinB = x12 * y23 + x32 * y12;
144 |
145 | long sl01 = x01 * x01 + y01 * y01;
146 | long sl03 = x03 * x03 + y03 * y03;
147 | long sl12 = x12 * x12 + y12 * y12;
148 | long sl23 = x32 * x32 + y23 * y23;
149 |
150 | double max0 = sl01 > sl03 ? sl01 : sl03;
151 | double max1 = sl12 > sl23 ? sl12 : sl23;
152 |
153 | double sinAB = ((double) sinA * (double) cosB + (double) cosA * (double) sinB) / (max0 * max1);
154 |
155 | return sinAB < 0.001;
156 | }
157 |
158 | public static bool IsCCW(IntVector a, IntVector b, IntVector c) {
159 | long m0 = (c.y - a.y) * (b.x - a.x);
160 | long m1 = (b.y - a.y) * (c.x - a.x);
161 |
162 | return m0 < m1;
163 | }
164 |
165 | }
166 |
167 | internal static class DelaunayExt {
168 | internal static bool Swap(this ref Delaunay delaunay, Triangle abc, Triangle pbc) {
169 | int ai = abc.Opposite(pbc.index); // opposite a-p
170 | int bi; // edge bc
171 | int ci;
172 |
173 | Vertex a, b, c;
174 |
175 | int acIndex;
176 |
177 | switch (ai) {
178 | case 0:
179 | bi = 1;
180 | ci = 2;
181 | a = abc.vA;
182 | b = abc.vB;
183 | c = abc.vC;
184 |
185 | acIndex = abc.nB;
186 | break;
187 | case 1:
188 | bi = 2;
189 | ci = 0;
190 | a = abc.vB;
191 | b = abc.vC;
192 | c = abc.vA;
193 |
194 | acIndex = abc.nC;
195 | break;
196 | default:
197 | bi = 0;
198 | ci = 1;
199 | a = abc.vC;
200 | b = abc.vA;
201 | c = abc.vB;
202 |
203 | acIndex = abc.nA;
204 | break;
205 | }
206 |
207 | var p = pbc.OppositeVertex(abc.index);
208 |
209 | bool isPrefect = Delaunay.IsDelaunay(p.point, c.point, a.point, b.point);
210 |
211 | if(isPrefect) {
212 | return false;
213 | }
214 |
215 | bool isABP_CCW = Delaunay.IsCCW(a.point, b.point, p.point);
216 |
217 | int bp = pbc.FindNeighbor(c.index);
218 | int cp = pbc.FindNeighbor(b.index);
219 | int ab = abc.Neighbor(ci);
220 | int ac = abc.Neighbor(bi);
221 |
222 | // abc -> abp
223 | Triangle abp;
224 |
225 | // pbc -> acp
226 | Triangle acp;
227 |
228 | if(isABP_CCW) {
229 | abp = new Triangle(abc.index, a, b, p) {
230 | nA = bp, // a - bp
231 | nB = pbc.index, // b - ap
232 | nC = ab // p - ab
233 | };
234 |
235 | acp = new Triangle(pbc.index, a, p, c) {
236 | nA = cp, // a - cp
237 | nB = ac, // p - ac
238 | nC = abc.index // c - ap
239 | };
240 | } else {
241 | abp = new Triangle(abc.index, a, p, b) {
242 | nA = bp, // a - bp
243 | nB = ab, // p - ab
244 | nC = pbc.index // b - ap
245 | };
246 |
247 | acp = new Triangle(pbc.index, a, c, p) {
248 | nA = cp, // a - cp
249 | nB = abc.index, // c - ap
250 | nC = ac // p - ac
251 | };
252 | }
253 |
254 | // fix neighbor's link
255 | // ab, cp didn't change neighbor
256 | // bc -> ap, so no changes
257 |
258 | // ac (abc) is now edge of acp
259 | // int acIndex = abc.GetNeighborByIndex(bi); // b - angle
260 | if(acIndex >= 0) {
261 | var neighbor = delaunay.triangles[acIndex];
262 | neighbor.UpdateOpposite(abc.index, acp.index);
263 | delaunay.triangles[acIndex] = neighbor;
264 | }
265 |
266 | // bp (pbc) is now edge of abp
267 | int bpIndex = pbc.FindNeighbor(c.index); // c - angle
268 | if(bpIndex >= 0) {
269 | var neighbor = delaunay.triangles[bpIndex];
270 | neighbor.UpdateOpposite(pbc.index, abp.index);
271 | delaunay.triangles[bpIndex] = neighbor;
272 | }
273 |
274 | delaunay.triangles[abc.index] = abp;
275 | delaunay.triangles[pbc.index] = acp;
276 |
277 | return true;
278 | }
279 |
280 | internal static void Fix(this ref Delaunay delaunay, ref IndexBuffer indexBuffer, NativeArray indices) {
281 | var origin = new NativeArray(indices, Allocator.Temp);
282 | var buffer = new DynamicArray(16, Allocator.Temp);
283 |
284 | while (origin.Length > 0) {
285 | buffer.RemoveAll();
286 | for (int ii = 0; ii < origin.Length; ++ii) {
287 | int i = origin[ii];
288 | var triangle = delaunay.triangles[i];
289 | for (int k = 0; k <= 2; ++k) {
290 | int neighborIndex = triangle.Neighbor(k);
291 | if (neighborIndex >= 0) {
292 | var neighbor = delaunay.triangles[neighborIndex];
293 |
294 | if (delaunay.Swap(triangle, neighbor)) {
295 |
296 | indexBuffer.Add(triangle.index);
297 | indexBuffer.Add(neighbor.index);
298 |
299 | triangle = delaunay.triangles[triangle.index];
300 | neighbor = delaunay.triangles[neighbor.index];
301 |
302 | if (triangle.nA >= 0 && triangle.nA != neighbor.index) {
303 | buffer.Add(triangle.nA);
304 | }
305 | if (triangle.nB >= 0 && triangle.nB != neighbor.index) {
306 | buffer.Add(triangle.nB);
307 | }
308 | if (triangle.nC >= 0 && triangle.nC != neighbor.index) {
309 | buffer.Add(triangle.nC);
310 | }
311 |
312 | if (neighbor.nA >= 0 && neighbor.nA != triangle.index) {
313 | buffer.Add(neighbor.nA);
314 | }
315 | if (neighbor.nB >= 0 && neighbor.nB != triangle.index) {
316 | buffer.Add(neighbor.nB);
317 | }
318 | if (neighbor.nC >= 0 && neighbor.nC != triangle.index) {
319 | buffer.Add(neighbor.nC);
320 | }
321 | }
322 | }
323 | }
324 | }
325 |
326 | origin.Dispose();
327 | origin = buffer.ToArray(Allocator.Temp);
328 | }
329 | }
330 | }
331 |
332 | }
333 |
--------------------------------------------------------------------------------
/Tests/Editor/Triangulation/ComplexDelaunayTests.cs:
--------------------------------------------------------------------------------
1 | using iShape.Geometry;
2 | using iShape.Geometry.Container;
3 | using iShape.Triangulation.Shape.Delaunay;
4 | using Tests.Triangulation.Util;
5 | using Tests.Triangulation.Data;
6 | using NUnit.Framework;
7 | using Unity.Collections;
8 | using UnityEngine;
9 | using Triangle = Tests.Triangulation.Util.Triangle;
10 |
11 | namespace Tests.Triangulation {
12 |
13 | public class ComplexDelaunayTests {
14 |
15 | private NativeArray Triangulate(int index) {
16 | var iGeom = IntGeom.DefGeom;
17 |
18 | var data = ComplexTests.data[index];
19 |
20 | var hull = iGeom.Int(data[0]);
21 | var holes = new IntVector[data.Length - 1][];
22 | for(int i = 1; i < data.Length; ++i) {
23 | holes[i - 1] = iGeom.Int(data[i]);
24 | }
25 |
26 | var iShape = new IntShape(hull, holes);
27 | var pShape = new PlainShape(iShape, Allocator.Temp);
28 |
29 | var triangles = pShape.DelaunayTriangulate(Allocator.Temp);
30 |
31 | Assert.IsTrue(Triangle.IsCCW(pShape.points, triangles));
32 |
33 | pShape.Dispose();
34 |
35 | return triangles;
36 | }
37 |
38 | [Test]
39 | public void TestTriangulate_00() {
40 | var triangles = this.Triangulate(0);
41 | var origin = new NativeArray(new int[] {
42 | 0, 1, 3,
43 | 1, 2, 3
44 | }, Allocator.Temp);
45 |
46 | bool isEqual = triangles.CompareTriangles(origin);
47 | Assert.IsTrue(isEqual);
48 | triangles.Dispose();
49 | origin.Dispose();
50 | }
51 |
52 | [Test]
53 | public void TestTriangulate_01() {
54 | var triangles = this.Triangulate(1);
55 | var origin = new NativeArray(new int[] {
56 | 1, 2, 0,
57 | 0, 2, 4,
58 | 2, 3, 4
59 | }, Allocator.Temp);
60 |
61 | bool isEqual = triangles.CompareTriangles(origin);
62 | Assert.IsTrue(isEqual);
63 | triangles.Dispose();
64 | origin.Dispose();
65 | }
66 |
67 | [Test]
68 | public void TestTriangulate_02() {
69 | var triangles = this.Triangulate(2);
70 | var origin = new NativeArray(new int[] {
71 | 0, 5, 6,
72 | 0, 6, 3,
73 | 6, 7, 3,
74 | 0, 1, 5,
75 | 1, 4, 5,
76 | 1, 7, 4,
77 | 1, 2, 7,
78 | 7, 2, 3
79 | }, Allocator.Temp);
80 |
81 | bool isEqual = triangles.CompareTriangles(origin);
82 | Assert.IsTrue(isEqual);
83 | triangles.Dispose();
84 | origin.Dispose();
85 | }
86 |
87 | [Test]
88 | public void TestTriangulate_03() {
89 | var triangles = this.Triangulate(3);
90 | var origin = new NativeArray(new int[] {
91 | 2, 3, 1,
92 | 0, 1, 3
93 | }, Allocator.Temp);
94 |
95 | bool isEqual = triangles.CompareTriangles(origin);
96 | Assert.IsTrue(isEqual);
97 | triangles.Dispose();
98 | origin.Dispose();
99 | }
100 |
101 | [Test]
102 | public void TestTriangulate_04() {
103 | var triangles = this.Triangulate(4);
104 | var origin = new NativeArray(new int[] {
105 | 1, 4, 0,
106 | 1, 2, 3,
107 | 1, 3, 4
108 | }, Allocator.Temp);
109 |
110 | bool isEqual = triangles.CompareTriangles(origin);
111 | Assert.IsTrue(isEqual);
112 | triangles.Dispose();
113 | origin.Dispose();
114 | }
115 |
116 | [Test]
117 | public void TestTriangulate_05() {
118 | var triangles = this.Triangulate(5);
119 | var origin = new NativeArray(new int[] {
120 | 1, 4, 0,
121 | 1, 2, 3,
122 | 1, 3, 4
123 | }, Allocator.Temp);
124 |
125 | bool isEqual = triangles.CompareTriangles(origin);
126 | Assert.IsTrue(isEqual);
127 | triangles.Dispose();
128 | origin.Dispose();
129 | }
130 |
131 | [Test]
132 | public void TestTriangulate_06() {
133 | var triangles = this.Triangulate(6);
134 | var origin = new NativeArray(new int[] {
135 | 0, 2, 3,
136 | 0, 1, 2
137 | }, Allocator.Temp);
138 |
139 | bool isEqual = triangles.CompareTriangles(origin);
140 | Assert.IsTrue(isEqual);
141 | triangles.Dispose();
142 | origin.Dispose();
143 | }
144 |
145 | [Test]
146 | public void TestTriangulate_07() {
147 | var triangles = this.Triangulate(7);
148 | var origin = new NativeArray(new int[] {
149 | 1, 7, 0,
150 | 7, 5, 6,
151 | 7, 1, 4,
152 | 3, 1, 2,
153 | 1, 3, 4,
154 | 7, 4, 5
155 | }, Allocator.Temp);
156 |
157 | bool isEqual = triangles.CompareTriangles(origin);
158 | Assert.IsTrue(isEqual);
159 | triangles.Dispose();
160 | origin.Dispose();
161 | }
162 |
163 | [Test]
164 | public void TestTriangulate_08() {
165 | var triangles = this.Triangulate(8);
166 | var origin = new NativeArray(new int[] {
167 | 7, 5, 6,
168 | 1, 2, 0,
169 | 7, 0, 2,
170 | 2, 3, 4,
171 | 2, 4, 7,
172 | 4, 5, 7
173 | }, Allocator.Temp);
174 |
175 | bool isEqual = triangles.CompareTriangles(origin);
176 | Assert.IsTrue(isEqual);
177 | triangles.Dispose();
178 | origin.Dispose();
179 | }
180 |
181 | [Test]
182 | public void TestTriangulate_09() {
183 | var triangles = this.Triangulate(9);
184 | var origin = new NativeArray(new int[] {
185 | 2, 0, 1,
186 | 3, 0, 2,
187 | 7, 5, 6,
188 | 0, 5, 7,
189 | 0, 3, 4,
190 | 0, 4, 5
191 | }, Allocator.Temp);
192 |
193 | bool isEqual = triangles.CompareTriangles(origin);
194 | Assert.IsTrue(isEqual);
195 | triangles.Dispose();
196 | origin.Dispose();
197 | }
198 |
199 | [Test]
200 | public void TestTriangulate_10() {
201 | var triangles = this.Triangulate(10);
202 | var origin = new NativeArray(new int[] {
203 | 7, 0, 6,
204 | 5, 6, 0,
205 | 4, 5, 0,
206 | 1, 2, 4,
207 | 1, 4, 0,
208 | 3, 4, 2
209 | }, Allocator.Temp);
210 |
211 | bool isEqual = triangles.CompareTriangles(origin);
212 | Assert.IsTrue(isEqual);
213 | triangles.Dispose();
214 | origin.Dispose();
215 | }
216 |
217 | [Test]
218 | public void TestTriangulate_11() {
219 | var triangles = this.Triangulate(11);
220 | var origin = new NativeArray(new int[] {
221 | 0, 1, 4,
222 | 0, 4, 7,
223 | 4, 5, 7,
224 | 7, 5, 6,
225 | 4, 1, 3,
226 | 1, 2, 3
227 | }, Allocator.Temp);
228 |
229 | bool isEqual = triangles.CompareTriangles(origin);
230 | Assert.IsTrue(isEqual);
231 | triangles.Dispose();
232 | origin.Dispose();
233 | }
234 |
235 | [Test]
236 | public void TestTriangulate_12() {
237 | var triangles = this.Triangulate(12);
238 | var origin = new NativeArray(new int[] {
239 | 0, 4, 7,
240 | 4, 5, 7,
241 | 6, 7, 5,
242 | 2, 3, 1,
243 | 0, 1, 4,
244 | 4, 1, 3
245 | }, Allocator.Temp);
246 |
247 | bool isEqual = triangles.CompareTriangles(origin);
248 | Assert.IsTrue(isEqual);
249 | triangles.Dispose();
250 | origin.Dispose();
251 | }
252 |
253 | [Test]
254 | public void TestTriangulate_13() {
255 | var triangles = this.Triangulate(13);
256 | var origin = new NativeArray(new int[] {
257 | 0, 1, 6,
258 | 0, 6, 11,
259 | 11, 9, 10,
260 | 7, 9, 11,
261 | 8, 9, 7,
262 | 7, 11, 6,
263 | 6, 1, 5,
264 | 1, 2, 3,
265 | 1, 3, 5,
266 | 5, 3, 4
267 | }, Allocator.Temp);
268 |
269 | bool isEqual = triangles.CompareTriangles(origin);
270 | Assert.IsTrue(isEqual);
271 | triangles.Dispose();
272 | origin.Dispose();
273 | }
274 |
275 | [Test]
276 | public void TestTriangulate_14() {
277 | var triangles = this.Triangulate(14);
278 | var origin = new NativeArray(new int[] {
279 | 9, 0, 1,
280 | 4, 9, 1,
281 | 8, 9, 5,
282 | 6, 7, 5,
283 | 8, 5, 7,
284 | 4, 5, 9,
285 | 1, 2, 3,
286 | 1, 3, 4
287 | }, Allocator.Temp);
288 |
289 | bool isEqual = triangles.CompareTriangles(origin);
290 | Assert.IsTrue(isEqual);
291 | triangles.Dispose();
292 | origin.Dispose();
293 | }
294 |
295 | [Test]
296 | public void TestTriangulate_15() {
297 | var triangles = this.Triangulate(15);
298 | var origin = new NativeArray(new int[] {
299 | 4, 2, 3,
300 | 4, 0, 2,
301 | 0, 1, 2
302 | }, Allocator.Temp);
303 |
304 | bool isEqual = triangles.CompareTriangles(origin);
305 | Assert.IsTrue(isEqual);
306 | triangles.Dispose();
307 | origin.Dispose();
308 | }
309 |
310 | [Test]
311 | public void TestTriangulate_16() {
312 | var triangles = this.Triangulate(16);
313 | var origin = new NativeArray(new int[] {
314 | 33, 1, 2,
315 | 20, 21, 0,
316 | 0, 35, 20,
317 | 27, 28, 16,
318 | 27, 16, 26,
319 | 29, 16, 28,
320 | 19, 16, 29,
321 | 15, 22, 14,
322 | 13, 14, 22,
323 | 23, 13, 22,
324 | 13, 23, 24,
325 | 25, 13, 24,
326 | 25, 12, 13,
327 | 25, 26, 10,
328 | 11, 12, 25,
329 | 16, 17, 26,
330 | 26, 17, 10,
331 | 25, 10, 11,
332 | 9, 10, 17,
333 | 18, 9, 17,
334 | 8, 9, 18,
335 | 21, 22, 15,
336 | 0, 21, 15,
337 | 1, 34, 0,
338 | 0, 34, 35,
339 | 33, 34, 1,
340 | 33, 2, 3,
341 | 33, 3, 32,
342 | 31, 32, 3,
343 | 30, 31, 3,
344 | 4, 30, 3,
345 | 5, 30, 4,
346 | 29, 30, 5,
347 | 29, 5, 19,
348 | 19, 5, 6,
349 | 19, 6, 18,
350 | 18, 6, 7,
351 | 18, 7, 8
352 | }, Allocator.Temp);
353 |
354 | bool isEqual = triangles.CompareTriangles(origin);
355 | Assert.IsTrue(isEqual);
356 | triangles.Dispose();
357 | origin.Dispose();
358 | }
359 |
360 | [Test]
361 | public void TestTriangulate_17() {
362 | var triangles = this.Triangulate(17);
363 | var origin = new NativeArray(new int[] {
364 | 6, 7, 8,
365 | 6, 8, 5,
366 | 0, 8, 7,
367 | 11, 8, 0,
368 | 1, 11, 0,
369 | 1, 10, 11,
370 | 8, 9, 5,
371 | 4, 5, 9,
372 | 10, 4, 9,
373 | 2, 10, 1,
374 | 10, 3, 4,
375 | 2, 3, 10
376 | }, Allocator.Temp);
377 |
378 | bool isEqual = triangles.CompareTriangles(origin);
379 | Assert.IsTrue(isEqual);
380 | triangles.Dispose();
381 | origin.Dispose();
382 | }
383 |
384 |
385 | [Test]
386 | public void TestTriangulate_18()
387 | {
388 | var triangles = this.Triangulate(18);
389 | var origin = new NativeArray(new int[] {
390 | 8, 4, 0,
391 | 4, 5, 3,
392 | 6, 3, 5,
393 | 4, 3, 0,
394 | 8, 0, 11,
395 | 0, 1, 11,
396 | 11, 1, 10,
397 | 10, 1, 2,
398 | 3, 6, 2,
399 | 10, 2, 6
400 | }, Allocator.Temp);
401 |
402 | bool isEqual = triangles.CompareTriangles(origin);
403 | Assert.IsTrue(isEqual);
404 | triangles.Dispose();
405 | origin.Dispose();
406 | }
407 |
408 | [Test]
409 | public void TestTriangulate_19()
410 | {
411 | var triangles = this.Triangulate(19);
412 | var origin = new NativeArray(new int[] {
413 | 6, 8, 9,
414 | 8, 6, 7,
415 | 6, 9, 5,
416 | 1, 2, 4,
417 | 1, 4, 5,
418 | 4, 2, 3
419 | }, Allocator.Temp);
420 |
421 | bool isEqual = triangles.CompareTriangles(origin);
422 | Assert.IsTrue(isEqual);
423 | triangles.Dispose();
424 | origin.Dispose();
425 | }
426 |
427 | [Test]
428 | public void TestTriangulate_20()
429 | {
430 | var triangles = this.Triangulate(20);
431 | var origin = new NativeArray(new int[] {
432 | 1, 3, 4,
433 | 3, 1, 2,
434 | 1, 4, 0,
435 | 6, 7, 9,
436 | 6, 9, 0,
437 | 9, 7, 8
438 | }, Allocator.Temp);
439 |
440 | bool isEqual = triangles.CompareTriangles(origin);
441 | Assert.IsTrue(isEqual);
442 | triangles.Dispose();
443 | origin.Dispose();
444 | }
445 |
446 | [Test]
447 | public void TestTriangulate_21()
448 | {
449 | var triangles = this.Triangulate(21);
450 | var origin = new NativeArray(new int[] {
451 | 4, 5, 0,
452 | 8, 9, 5,
453 | 5, 9, 0,
454 | 10, 3, 9,
455 | 9, 3, 0,
456 | 0, 1, 4,
457 | 4, 1, 7,
458 | 8, 7, 11,
459 | 7, 1, 11,
460 | 3, 10, 2,
461 | 10, 11, 2,
462 | 11, 1, 2
463 | }, Allocator.Temp);
464 |
465 | bool isEqual = triangles.CompareTriangles(origin);
466 | Assert.IsTrue(isEqual);
467 | triangles.Dispose();
468 | origin.Dispose();
469 | }
470 |
471 | [Test]
472 | public void TestTriangulate_22()
473 | {
474 | var triangles = this.Triangulate(22);
475 | var origin = new NativeArray(new int[] {
476 | 4, 5, 8,
477 | 10, 5, 6,
478 | 8, 9, 3,
479 | 6, 2, 10,
480 | 10, 2, 9,
481 | 9, 2, 3,
482 | 3, 0, 8,
483 | 8, 0, 4,
484 | 4, 0, 7,
485 | 2, 6, 1,
486 | 6, 7, 1,
487 | 7, 0, 1
488 | }, Allocator.Temp);
489 |
490 | bool isEqual = triangles.CompareTriangles(origin);
491 | Assert.IsTrue(isEqual);
492 | triangles.Dispose();
493 | origin.Dispose();
494 | }
495 |
496 | [Test]
497 | public void TestTriangulate_23()
498 | {
499 | var triangles = this.Triangulate(23);
500 | var origin = new NativeArray(new int[] {
501 | 8, 7, 4,
502 | 6, 7, 10,
503 | 4, 5, 3,
504 | 10, 2, 6,
505 | 6, 2, 5,
506 | 5, 2, 3,
507 | 3, 0, 4,
508 | 4, 0, 8,
509 | 8, 0, 11,
510 | 2, 10, 1,
511 | 10, 11, 1,
512 | 11, 0, 1
513 | }, Allocator.Temp);
514 |
515 | bool isEqual = triangles.CompareTriangles(origin);
516 | Assert.IsTrue(isEqual);
517 | triangles.Dispose();
518 | origin.Dispose();
519 | }
520 |
521 | [Test]
522 | public void TestTriangulate_24()
523 | {
524 | var triangles = this.Triangulate(24);
525 | var origin = new NativeArray(new int[] {
526 | 6, 0, 7,
527 | 0, 1, 7,
528 | 1, 2, 7,
529 | 9, 2, 3,
530 | 7, 8, 6,
531 | 3, 4, 9,
532 | 4, 5, 9,
533 | 9, 5, 8,
534 | 8, 5, 6
535 | }, Allocator.Temp);
536 |
537 | bool isEqual = triangles.CompareTriangles(origin);
538 | Assert.IsTrue(isEqual);
539 | triangles.Dispose();
540 | origin.Dispose();
541 | }
542 |
543 | [Test]
544 | public void TestTriangulate_25()
545 | {
546 | var triangles = this.Triangulate(25);
547 | var origin = new NativeArray(new int[] {
548 | 7, 4, 5,
549 | 4, 9, 3,
550 | 2, 3, 9,
551 | 6, 0, 7,
552 | 6, 7, 5,
553 | 7, 0, 10,
554 | 2, 9, 1,
555 | 9, 10, 1,
556 | 10, 0, 1
557 | }, Allocator.Temp);
558 |
559 | bool isEqual = triangles.CompareTriangles(origin);
560 | Assert.IsTrue(isEqual);
561 | triangles.Dispose();
562 | origin.Dispose();
563 | }
564 |
565 | [Test]
566 | public void TestTriangulate_26()
567 | {
568 | var triangles = this.Triangulate(26);
569 | var origin = new NativeArray(new int[] {
570 | 9, 0, 10,
571 | 9, 10, 8,
572 | 0, 1, 10,
573 | 1, 2, 10,
574 | 12, 2, 3,
575 | 10, 7, 8,
576 | 7, 12, 6,
577 | 4, 12, 3,
578 | 12, 5, 6,
579 | 4, 5, 12
580 | }, Allocator.Temp);
581 |
582 | bool isEqual = triangles.CompareTriangles(origin);
583 | Assert.IsTrue(isEqual);
584 | triangles.Dispose();
585 | origin.Dispose();
586 | }
587 |
588 | [Test]
589 | public void TestTriangulate_27()
590 | {
591 | var triangles = this.Triangulate(27);
592 | var origin = new NativeArray(new int[] {
593 | 6, 0, 2,
594 | 2, 0, 1,
595 | 2, 3, 6,
596 | 6, 3, 5,
597 | 5, 3, 1
598 | }, Allocator.Temp);
599 |
600 | bool isEqual = triangles.CompareTriangles(origin);
601 | Assert.IsTrue(isEqual);
602 | triangles.Dispose();
603 | origin.Dispose();
604 | }
605 |
606 | [Test]
607 | public void TestTriangulate_28()
608 | {
609 | var triangles = this.Triangulate(28);
610 | var origin = new NativeArray(new int[] {
611 | 4, 5, 3,
612 | 5, 6, 3,
613 | 3, 6, 2,
614 | 4, 2, 0,
615 | 2, 6, 0
616 | }, Allocator.Temp);
617 |
618 | bool isEqual = triangles.CompareTriangles(origin);
619 | Assert.IsTrue(isEqual);
620 | triangles.Dispose();
621 | origin.Dispose();
622 | }
623 |
624 | [Test]
625 | public void TestTriangulate_29()
626 | {
627 | var triangles = this.Triangulate(29);
628 | var origin = new NativeArray(new int[] {
629 | 1, 2, 3,
630 | 2, 6, 4,
631 | 0, 4, 6,
632 | 0, 3, 4,
633 | 3, 0, 1
634 | }, Allocator.Temp);
635 |
636 | bool isEqual = triangles.CompareTriangles(origin);
637 | Assert.IsTrue(isEqual);
638 | triangles.Dispose();
639 | origin.Dispose();
640 | }
641 |
642 | [Test]
643 | public void TestTriangulate_30()
644 | {
645 | var triangles = this.Triangulate(30);
646 | var origin = new NativeArray(new int[] {
647 | 3, 1, 6,
648 | 1, 2, 5,
649 | 1, 5, 6,
650 | 5, 2, 4,
651 | 2, 3, 4
652 | }, Allocator.Temp);
653 |
654 | bool isEqual = triangles.CompareTriangles(origin);
655 | Assert.IsTrue(isEqual);
656 | triangles.Dispose();
657 | origin.Dispose();
658 | }
659 |
660 | [Test]
661 | public void TestTriangulate_33()
662 | {
663 | var triangles = this.Triangulate(33);
664 | var origin = new NativeArray(new int[] {
665 | 2, 4, 6,
666 | 6, 4, 5,
667 | 2, 3, 4,
668 | 6, 0, 2,
669 | 2, 0, 1
670 | }, Allocator.Temp);
671 |
672 | bool isEqual = triangles.CompareTriangles(origin);
673 | Assert.IsTrue(isEqual);
674 | triangles.Dispose();
675 | origin.Dispose();
676 | }
677 |
678 | [Test]
679 | public void TestTriangulate_34()
680 | {
681 | var triangles = this.Triangulate(34);
682 | var origin = new NativeArray(new int[] {
683 | 16, 17, 15,
684 | 21, 22, 20,
685 | 20, 22, 19,
686 | 22, 23, 19,
687 | 23, 24, 19,
688 | 19, 24, 18,
689 | 17, 18, 25,
690 | 24, 25, 18,
691 | 17, 25, 15,
692 | 12, 13, 11,
693 | 6, 8, 10,
694 | 10, 8, 9,
695 | 6, 7, 8,
696 | 26, 0, 1,
697 | 1, 2, 3,
698 | 14, 15, 13,
699 | 10, 15, 25,
700 | 10, 13, 15,
701 | 13, 10, 11,
702 | 10, 25, 6,
703 | 25, 26, 6,
704 | 26, 1, 3,
705 | 26, 3, 6,
706 | 6, 3, 5,
707 | 3, 4, 5
708 | }, Allocator.Temp);
709 |
710 | bool isEqual = triangles.CompareTriangles(origin);
711 | Assert.IsTrue(isEqual);
712 | triangles.Dispose();
713 | origin.Dispose();
714 | }
715 | }
716 | }
717 |
--------------------------------------------------------------------------------
/Readme/star_triangle.svg:
--------------------------------------------------------------------------------
1 |
2 |
388 |
--------------------------------------------------------------------------------