com.wallstop-studios.unity-helpers 2.0.0-rc56 → 2.0.0-rc58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.config/dotnet-tools.json +9 -9
- package/.editorconfig +184 -184
- package/.gitattributes +63 -63
- package/.github/workflows/npm-publish.yml +66 -66
- package/.pre-commit-config.yaml +21 -21
- package/Editor/AnimationCopier.cs +181 -181
- package/Editor/AnimationCopier.cs.meta +2 -2
- package/Editor/AnimationCreator.cs +253 -253
- package/Editor/AnimationCreator.cs.meta +11 -11
- package/Editor/AnimationEventEditor.cs +887 -887
- package/Editor/AnimatorControllerCopier.cs +162 -162
- package/Editor/AnimatorControllerCopier.cs.meta +2 -2
- package/Editor/CustomEditors/MatchColliderToSpriteEditor.cs +34 -34
- package/Editor/CustomEditors/MatchColliderToSpriteEditor.cs.meta +2 -2
- package/Editor/CustomEditors.meta +2 -2
- package/Editor/FitTextureSizeWizard.cs +147 -147
- package/Editor/FitTextureSizeWizard.cs.meta +2 -2
- package/Editor/PrefabCheckWizard.cs +170 -170
- package/Editor/PrefabCheckWizard.cs.meta +11 -11
- package/Editor/SpriteSettingsApplier.cs +272 -272
- package/Editor/SpriteSettingsApplier.cs.meta +2 -2
- package/Editor/StringInListeDrawer.cs +56 -56
- package/Editor/TextureResizerWizard.cs +181 -181
- package/Editor/TextureResizerWizard.cs.meta +2 -2
- package/Editor/TextureSettingsApplier.cs +178 -178
- package/Editor/TextureSettingsApplier.cs.meta +2 -2
- package/Editor/Utils/DxReadOnlyPropertyDrawer.cs +26 -26
- package/Editor/Utils/DxReadOnlyPropertyDrawer.cs.meta +11 -11
- package/Editor/Utils/EditorUtilities.cs +22 -22
- package/Editor/Utils/EditorUtilities.cs.meta +11 -11
- package/Editor/Utils.meta +8 -8
- package/Editor/WShowIfPropertyDrawer.cs +63 -63
- package/Editor/WallstopStudios.UnityHelpers.Editor.asmdef +17 -17
- package/Editor/WallstopStudios.UnityHelpers.Editor.asmdef.meta +7 -7
- package/LICENSE +21 -21
- package/LICENSE.md +6 -6
- package/LICENSE.meta +7 -7
- package/README.md +177 -177
- package/Runtime/Binaries/Microsoft.Bcl.AsyncInterfaces.dll.meta +33 -33
- package/Runtime/Binaries/Microsoft.Bcl.AsyncInterfaces.xml +223 -223
- package/Runtime/Binaries/Microsoft.Bcl.AsyncInterfaces.xml.meta +7 -7
- package/Runtime/Binaries/System.Text.Encodings.Web.dll.meta +33 -33
- package/Runtime/Binaries/System.Text.Encodings.Web.xml +935 -935
- package/Runtime/Binaries/System.Text.Encodings.Web.xml.meta +7 -7
- package/Runtime/Binaries/System.Text.Json.dll.meta +33 -33
- package/Runtime/Binaries/System.Text.Json.xml +4829 -4829
- package/Runtime/Binaries/System.Text.Json.xml.meta +7 -7
- package/Runtime/Binaries.meta +8 -8
- package/Runtime/Core/Attributes/AnimationEventAttribute.cs +131 -131
- package/Runtime/Core/Attributes/ChildComponentAttribute.cs +209 -209
- package/Runtime/Core/Attributes/DxReadOnlyAttribute.cs +6 -6
- package/Runtime/Core/Attributes/KSerializableAttribute.cs +19 -19
- package/Runtime/Core/Attributes/NotNullAttribute.cs +32 -32
- package/Runtime/Core/Attributes/ParentComponent.cs +185 -185
- package/Runtime/Core/Attributes/RelationalComponentExtensions.cs +14 -14
- package/Runtime/Core/Attributes/SiblingComponentAttribute.cs +117 -117
- package/Runtime/Core/Attributes/SiblingComponentAttribute.cs.meta +11 -11
- package/Runtime/Core/Attributes/ValidateAssignmentAttribute.cs +101 -101
- package/Runtime/Core/Attributes/ValidateAssignmentAttribute.cs.meta +11 -11
- package/Runtime/Core/Attributes/WShowIfAttribute.cs +16 -16
- package/Runtime/Core/Attributes.meta +8 -8
- package/Runtime/Core/DataStructure/Adapters/FastVector2Int.cs +92 -92
- package/Runtime/Core/DataStructure/Adapters/FastVector3Int.cs +185 -185
- package/Runtime/Core/DataStructure/Adapters/KGuid.cs +305 -305
- package/Runtime/Core/DataStructure/Adapters/KVector2.cs +80 -80
- package/Runtime/Core/DataStructure/Circle.cs +50 -50
- package/Runtime/Core/DataStructure/CyclicBuffer.cs +153 -153
- package/Runtime/Core/DataStructure/ISpatialTree.cs +60 -60
- package/Runtime/Core/DataStructure/ISpatialTree.cs.meta +11 -11
- package/Runtime/Core/DataStructure/KDTree.cs +292 -292
- package/Runtime/Core/DataStructure/KDTree.cs.meta +11 -11
- package/Runtime/Core/DataStructure/QuadTree.cs +287 -287
- package/Runtime/Core/DataStructure/RTree.cs +346 -346
- package/Runtime/Core/DataStructure/RTree.cs.meta +11 -11
- package/Runtime/Core/DataStructure/StringWrapper.cs +91 -91
- package/Runtime/Core/DataStructure/TimedCache.cs +66 -66
- package/Runtime/Core/Extension/AnimatorExtensions.cs +25 -25
- package/Runtime/Core/Extension/AsyncOperationExtensions.cs +110 -108
- package/Runtime/Core/Extension/CircleExtensions.cs +25 -25
- package/Runtime/Core/Extension/ColorExtensions.cs +629 -629
- package/Runtime/Core/Extension/DictionaryExtensions.cs +279 -279
- package/Runtime/Core/Extension/DirectionExtensions.cs +213 -213
- package/Runtime/Core/Extension/EnumExtensions.cs +37 -37
- package/Runtime/Core/Extension/EnumExtensions.cs.meta +2 -2
- package/Runtime/Core/Extension/HashSetExtensions.cs +12 -12
- package/Runtime/Core/Extension/IEnumerableExtensions.cs +122 -122
- package/Runtime/Core/Extension/IListExtensions.cs +106 -106
- package/Runtime/Core/Extension/LoggingExtensions.cs +258 -258
- package/Runtime/Core/Extension/RandomExtensions.cs +109 -109
- package/Runtime/Core/Extension/SerializedPropertyExtensions.cs +157 -157
- package/Runtime/Core/Extension/StringExtensions.cs +151 -151
- package/Runtime/Core/Extension/UnityExtensions.cs +1608 -1608
- package/Runtime/Core/Helper/ArrayConverter.cs +39 -39
- package/Runtime/Core/Helper/ArrayConverter.cs.meta +2 -2
- package/Runtime/Core/Helper/AssignUtilities.cs +14 -14
- package/Runtime/Core/Helper/AssignUtilities.cs.meta +11 -11
- package/Runtime/Core/Helper/Enumerables.cs +17 -17
- package/Runtime/Core/Helper/FormattingHelpers.cs +32 -32
- package/Runtime/Core/Helper/Geometry.cs +43 -43
- package/Runtime/Core/Helper/Helpers.cs +722 -722
- package/Runtime/Core/Helper/Helpers.cs.meta +11 -11
- package/Runtime/Core/Helper/IterationHelpers.cs +32 -32
- package/Runtime/Core/Helper/IterationHelpers.cs.meta +11 -11
- package/Runtime/Core/Helper/LifetimeHelpers.cs +13 -13
- package/Runtime/Core/Helper/Objects.cs +769 -769
- package/Runtime/Core/Helper/Partials/LogHelpers.cs +13 -13
- package/Runtime/Core/Helper/Partials/LogHelpers.cs.meta +2 -2
- package/Runtime/Core/Helper/Partials/MathHelpers.cs +30 -30
- package/Runtime/Core/Helper/Partials/MathHelpers.cs.meta +2 -2
- package/Runtime/Core/Helper/Partials/ObjectHelpers.cs +388 -388
- package/Runtime/Core/Helper/Partials/ObjectHelpers.cs.meta +2 -2
- package/Runtime/Core/Helper/Partials/TransformHelpers.cs +189 -189
- package/Runtime/Core/Helper/Partials/TransformHelpers.cs.meta +2 -2
- package/Runtime/Core/Helper/Partials.meta +2 -2
- package/Runtime/Core/Helper/ReflectionHelpers.cs +452 -452
- package/Runtime/Core/Helper/ReflectionHelpers.cs.meta +2 -2
- package/Runtime/Core/Helper/SceneHelper.cs +209 -213
- package/Runtime/Core/Helper/SpriteHelpers.cs +41 -41
- package/Runtime/Core/Helper/SpriteHelpers.cs.meta +11 -11
- package/Runtime/Core/Helper/StringInList.cs +31 -31
- package/Runtime/Core/Helper/StringInList.cs.meta +11 -11
- package/Runtime/Core/Helper/UnityMainThreadDispatcher.cs +82 -80
- package/Runtime/Core/Helper/WallMath.cs +166 -166
- package/Runtime/Core/Math/Line.cs +55 -55
- package/Runtime/Core/Math/Parabola.cs +47 -47
- package/Runtime/Core/Math/PointPolygonCheck.cs +36 -36
- package/Runtime/Core/Math/PointPolygonCheck.cs.meta +11 -11
- package/Runtime/Core/Math/Range.cs +92 -92
- package/Runtime/Core/Math/XXHash.cs +310 -310
- package/Runtime/Core/Math/XXHash.cs.meta +11 -11
- package/Runtime/Core/Model/Direction.cs +43 -43
- package/Runtime/Core/OneOf/FastOneOf.cs +152 -152
- package/Runtime/Core/OneOf/None.cs +4 -4
- package/Runtime/Core/Random/AbstractRandom.cs +585 -585
- package/Runtime/Core/Random/DotNetRandom.cs +54 -54
- package/Runtime/Core/Random/DotNetRandom.cs.meta +2 -2
- package/Runtime/Core/Random/IRandom.cs +161 -161
- package/Runtime/Core/Random/LinearCongruentialGenerator.cs +49 -49
- package/Runtime/Core/Random/NativePcgRandom.cs +97 -97
- package/Runtime/Core/Random/PRNG.cs +7 -7
- package/Runtime/Core/Random/PRNG.cs.meta +2 -2
- package/Runtime/Core/Random/PcgRandom.cs +149 -149
- package/Runtime/Core/Random/PerlinNoise.cs +369 -369
- package/Runtime/Core/Random/PerlinNoise.cs.meta +2 -2
- package/Runtime/Core/Random/RandomState.cs +131 -131
- package/Runtime/Core/Random/RandomUtilities.cs +26 -26
- package/Runtime/Core/Random/RandomUtilities.cs.meta +11 -11
- package/Runtime/Core/Random/RomuDuo.cs +116 -116
- package/Runtime/Core/Random/RomuDuo.cs.meta +2 -2
- package/Runtime/Core/Random/SplitMix64.cs +94 -94
- package/Runtime/Core/Random/SplitMix64.cs.meta +2 -2
- package/Runtime/Core/Random/SquirrelRandom.cs +84 -84
- package/Runtime/Core/Random/SystemRandom.cs +162 -162
- package/Runtime/Core/Random/ThreadLocalRandom.cs +12 -12
- package/Runtime/Core/Random/UnityRandom.cs +57 -57
- package/Runtime/Core/Random/UnityRandom.cs.meta +11 -11
- package/Runtime/Core/Random/WyRandom.cs +121 -121
- package/Runtime/Core/Random/WyRandom.cs.meta +2 -2
- package/Runtime/Core/Random/XorShiftRandom.cs +52 -52
- package/Runtime/Core/Random/XorShiftRandom.cs.meta +11 -11
- package/Runtime/Core/Random/XorShiroRandom.cs +119 -119
- package/Runtime/Core/Random/XorShiroRandom.cs.meta +2 -2
- package/Runtime/Core/Serialization/JsonConverters/ColorConverter.cs +88 -88
- package/Runtime/Core/Serialization/JsonConverters/GameObjectConverter.cs +37 -37
- package/Runtime/Core/Serialization/JsonConverters/Matrix4x4Converter.cs +218 -218
- package/Runtime/Core/Serialization/JsonConverters/TypeConverter.cs +28 -28
- package/Runtime/Core/Serialization/JsonConverters/Vector2Converter.cs +74 -74
- package/Runtime/Core/Serialization/JsonConverters/Vector3Converter.cs +81 -81
- package/Runtime/Core/Serialization/JsonConverters/Vector4Converter.cs +88 -88
- package/Runtime/Core/Serialization/Serializer.cs +195 -195
- package/Runtime/Core/Threading/SingleThreadedThreadPool.cs +113 -113
- package/Runtime/Protobuf-Net/System.Buffers.dll.meta +33 -33
- package/Runtime/Protobuf-Net/System.Collections.Immutable.dll.meta +33 -33
- package/Runtime/Protobuf-Net/System.Collections.Immutable.xml +5379 -5379
- package/Runtime/Protobuf-Net/System.Collections.Immutable.xml.meta +7 -7
- package/Runtime/Protobuf-Net/System.Numerics.Vectors.dll.meta +33 -33
- package/Runtime/Protobuf-Net/System.Runtime.CompilerServices.Unsafe.dll.meta +33 -33
- package/Runtime/Protobuf-Net/System.Runtime.CompilerServices.Unsafe.xml +290 -290
- package/Runtime/Protobuf-Net/System.Runtime.CompilerServices.Unsafe.xml.meta +7 -7
- package/Runtime/Protobuf-Net/protobuf-net.Core.dll.meta +33 -33
- package/Runtime/Protobuf-Net/protobuf-net.dll.meta +33 -33
- package/Runtime/UI/LayeredImage.cs +364 -364
- package/Runtime/UI/LayeredImage.cs.meta +2 -2
- package/Runtime/UI.meta +2 -2
- package/Runtime/Utils/AnimationEventEqualityComparer.cs +161 -161
- package/Runtime/Utils/AnimatorEnumStateMachine.cs +88 -88
- package/Runtime/Utils/Buffers.cs +33 -33
- package/Runtime/Utils/CenterPointOffset.cs +30 -30
- package/Runtime/Utils/CenterPointOffset.cs.meta +2 -2
- package/Runtime/Utils/CircleLineRenderer.cs +134 -134
- package/Runtime/Utils/CoroutineHandler.cs +4 -4
- package/Runtime/Utils/CoroutineHandler.cs.meta +2 -2
- package/Runtime/Utils/DeferredDisposalResult.cs +23 -23
- package/Runtime/Utils/MatchColliderToSprite.cs +94 -94
- package/Runtime/Utils/MatchColliderToSprite.cs.meta +2 -2
- package/Runtime/Utils/Oscillator.cs +27 -27
- package/Runtime/Utils/RuntimeSingleton.cs +69 -69
- package/Runtime/Utils/RuntimeSingleton.cs.meta +11 -11
- package/Runtime/Utils/SetTextureImportData.cs +69 -69
- package/Runtime/Utils/SpriteRendererMetadata.cs +374 -374
- package/Runtime/Utils/SpriteRendererMetadata.cs.meta +2 -2
- package/Runtime/Utils/SpriteRendererSyncer.cs +100 -100
- package/Runtime/Utils/SpriteRendererSyncer.cs.meta +2 -2
- package/Runtime/Utils/TextureScale.cs +179 -179
- package/Runtime/Utils/TextureScale.cs.meta +2 -2
- package/Runtime/WallstopStudios.UnityHelpers.asmdef +13 -13
- package/Tests/Runtime/Attributes/ChildComponentTests.cs +81 -81
- package/Tests/Runtime/Attributes/Components/ExpectChildSpriteRenderers.cs +28 -28
- package/Tests/Runtime/Attributes/Components/ExpectParentSpriteRenderers.cs +28 -28
- package/Tests/Runtime/Attributes/ParentComponentTests.cs +68 -68
- package/Tests/Runtime/Components/RelationalComponentTesterComplex.cs +34 -34
- package/Tests/Runtime/Components/RelationalComponentTesterComplex.cs.meta +2 -2
- package/Tests/Runtime/Components/RelationalComponentsTesterSimple.cs +40 -40
- package/Tests/Runtime/Components.meta +2 -2
- package/Tests/Runtime/DataStructures/BalancedKDTreeTests.cs +14 -14
- package/Tests/Runtime/DataStructures/BalancedKDTreeTests.cs.meta +11 -11
- package/Tests/Runtime/DataStructures/CyclicBufferTests.cs +324 -324
- package/Tests/Runtime/DataStructures/QuadTreeTests.cs +14 -14
- package/Tests/Runtime/DataStructures/QuadTreeTests.cs.meta +11 -11
- package/Tests/Runtime/DataStructures/SpatialTreeTests.cs +130 -130
- package/Tests/Runtime/DataStructures/SpatialTreeTests.cs.meta +11 -11
- package/Tests/Runtime/DataStructures/UnbalancedKDTreeTests.cs +14 -14
- package/Tests/Runtime/DataStructures/UnbalancedKDTreeTests.cs.meta +11 -11
- package/Tests/Runtime/DataStructures.meta +8 -8
- package/Tests/Runtime/Extensions/DictionaryExtensionTests.cs +439 -439
- package/Tests/Runtime/Extensions/DictionaryExtensionTests.cs.meta +2 -2
- package/Tests/Runtime/Extensions/EnumExtensionTests.cs +128 -128
- package/Tests/Runtime/Extensions/EnumExtensionTests.cs.meta +2 -2
- package/Tests/Runtime/Extensions/IListExtensionTests.cs +104 -104
- package/Tests/Runtime/Extensions/RandomExtensionTests.cs +27 -27
- package/Tests/Runtime/Extensions/RandomExtensionTests.cs.meta +2 -2
- package/Tests/Runtime/Extensions/StringExtensionTests.cs +31 -31
- package/Tests/Runtime/Extensions/StringExtensionTests.cs.meta +2 -2
- package/Tests/Runtime/Extensions.meta +2 -2
- package/Tests/Runtime/Helper/ArrayConverterTests.cs +19 -19
- package/Tests/Runtime/Helper/ArrayConverterTests.cs.meta +2 -2
- package/Tests/Runtime/Helper/FormattingHelperTests.cs +129 -129
- package/Tests/Runtime/Helper/FormattingHelperTests.cs.meta +2 -2
- package/Tests/Runtime/Helper/ObjectHelperTests.cs +402 -402
- package/Tests/Runtime/Helper/ObjectHelperTests.cs.meta +2 -2
- package/Tests/Runtime/Helper/ReflectionHelperTests.cs +536 -536
- package/Tests/Runtime/Helper/SceneHelperTests.cs +94 -94
- package/Tests/Runtime/Helper/WallMathTests.cs +233 -233
- package/Tests/Runtime/Helper/WallMathTests.cs.meta +2 -2
- package/Tests/Runtime/Helper.meta +2 -2
- package/Tests/Runtime/Performance/KDTreePerformanceTests.cs +14 -14
- package/Tests/Runtime/Performance/KDTreePerformanceTests.cs.meta +11 -11
- package/Tests/Runtime/Performance/QuadTreePerformanceTests.cs +14 -14
- package/Tests/Runtime/Performance/QuadTreePerformanceTests.cs.meta +11 -11
- package/Tests/Runtime/Performance/RandomPerformanceTests.cs +157 -157
- package/Tests/Runtime/Performance/RandomPerformanceTests.cs.meta +11 -11
- package/Tests/Runtime/Performance/RelationComponentPerformanceTests.cs +61 -61
- package/Tests/Runtime/Performance/RelationComponentPerformanceTests.cs.meta +2 -2
- package/Tests/Runtime/Performance/SpatialTreePerformanceTest.cs +154 -154
- package/Tests/Runtime/Performance/SpatialTreePerformanceTest.cs.meta +11 -11
- package/Tests/Runtime/Performance/UnbalancedKDTreeTests.cs +14 -14
- package/Tests/Runtime/Performance/UnbalancedKDTreeTests.cs.meta +11 -11
- package/Tests/Runtime/Performance.meta +8 -8
- package/Tests/Runtime/Random/DotNetRandomTests.cs +9 -9
- package/Tests/Runtime/Random/DotNetRandomTests.cs.meta +2 -2
- package/Tests/Runtime/Random/LinearCongruentialGeneratorTests.cs +12 -12
- package/Tests/Runtime/Random/PcgRandomTests.cs +9 -9
- package/Tests/Runtime/Random/PcgRandomTests.cs.meta +11 -11
- package/Tests/Runtime/Random/RandomTestBase.cs +787 -787
- package/Tests/Runtime/Random/RandomTestBase.cs.meta +11 -11
- package/Tests/Runtime/Random/RomuDuoRandomTests.cs +9 -9
- package/Tests/Runtime/Random/RomuDuoRandomTests.cs.meta +2 -2
- package/Tests/Runtime/Random/SplitMix64RandomTests.cs +9 -9
- package/Tests/Runtime/Random/SplitMix64RandomTests.cs.meta +2 -2
- package/Tests/Runtime/Random/SquirrelRandomTests.cs +14 -14
- package/Tests/Runtime/Random/SquirrelRandomTests.cs.meta +11 -11
- package/Tests/Runtime/Random/SystemRandomTests.cs +10 -10
- package/Tests/Runtime/Random/SystemRandomTests.cs.meta +11 -11
- package/Tests/Runtime/Random/UnityRandomTests.cs +9 -9
- package/Tests/Runtime/Random/UnityRandomTests.cs.meta +11 -11
- package/Tests/Runtime/Random/WyRandomTests.cs +9 -9
- package/Tests/Runtime/Random/WyRandomTests.cs.meta +2 -2
- package/Tests/Runtime/Random/XorShiftRandomTests.cs +9 -9
- package/Tests/Runtime/Random/XorShiftRandomTests.cs.meta +11 -11
- package/Tests/Runtime/Random/XorShiroRandomTests.cs +9 -9
- package/Tests/Runtime/Random/XorShiroRandomTests.cs.meta +2 -2
- package/Tests/Runtime/Random.meta +8 -8
- package/Tests/Runtime/Serialization/JsonSerializationTest.cs +156 -156
- package/Tests/Runtime/Serialization/JsonSerializationTest.cs.meta +2 -2
- package/Tests/Runtime/Serialization.meta +2 -2
- package/Tests/Runtime/Utils/SpriteRendererMetadataTests.cs +399 -399
- package/Tests/Runtime/Utils/SpriteRendererMetadataTests.cs.meta +2 -2
- package/Tests/Runtime/Utils.meta +2 -2
- package/Tests/Runtime/WallstopStudios.UnityHelpers.Tests.Runtime.asmdef +22 -22
- package/Tests/Runtime/WallstopStudios.UnityHelpers.Tests.Runtime.asmdef.meta +7 -7
- package/Tests/Runtime.meta +8 -8
- package/package.json +38 -38
|
@@ -1,1608 +1,1608 @@
|
|
|
1
|
-
namespace UnityHelpers.Core.Extension
|
|
2
|
-
{
|
|
3
|
-
using System;
|
|
4
|
-
using System.Collections.Generic;
|
|
5
|
-
using System.Linq;
|
|
6
|
-
using System.Threading.Tasks;
|
|
7
|
-
using DataStructure;
|
|
8
|
-
using DataStructure.Adapters;
|
|
9
|
-
using Helper;
|
|
10
|
-
using Random;
|
|
11
|
-
using UnityEditor;
|
|
12
|
-
using UnityEngine;
|
|
13
|
-
using UnityEngine.UI;
|
|
14
|
-
using Utils;
|
|
15
|
-
|
|
16
|
-
public static class UnityExtensions
|
|
17
|
-
{
|
|
18
|
-
public static Vector2 GetCenter(this GameObject gameObject)
|
|
19
|
-
{
|
|
20
|
-
if (gameObject.TryGetComponent(out CenterPointOffset centerPointOffset))
|
|
21
|
-
{
|
|
22
|
-
return centerPointOffset.CenterPoint;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return gameObject.transform.position;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
public static Bounds Bounds(this Rect rect)
|
|
29
|
-
{
|
|
30
|
-
return new Bounds(rect.center, rect.size);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
public static Rect Rect(this Bounds bounds)
|
|
34
|
-
{
|
|
35
|
-
return new Rect(bounds.center - bounds.extents, bounds.size);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
public static Rect GetWorldRect(this RectTransform transform)
|
|
39
|
-
{
|
|
40
|
-
Vector3[] fourCorners = new Vector3[4];
|
|
41
|
-
transform.GetWorldCorners(fourCorners);
|
|
42
|
-
|
|
43
|
-
float[] xValues = fourCorners.Select(vector => vector.x).ToArray();
|
|
44
|
-
float[] yValues = fourCorners.Select(vector => vector.y).ToArray();
|
|
45
|
-
float minX = Mathf.Min(xValues);
|
|
46
|
-
float maxX = Mathf.Max(xValues);
|
|
47
|
-
float minY = Mathf.Min(yValues);
|
|
48
|
-
float maxY = Mathf.Max(yValues);
|
|
49
|
-
|
|
50
|
-
return new Rect(minX, minY, maxX - minX, maxY - minY);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
public static Bounds OrthographicBounds(this Camera camera)
|
|
54
|
-
{
|
|
55
|
-
float screenAspect = (float)Screen.width / Screen.height;
|
|
56
|
-
float cameraHeight = camera.orthographicSize * 2;
|
|
57
|
-
Bounds bounds = new(
|
|
58
|
-
(Vector2)camera.transform.position,
|
|
59
|
-
new Vector3(cameraHeight * screenAspect, cameraHeight, 1)
|
|
60
|
-
);
|
|
61
|
-
return bounds;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
public static string ToJsonString(this Vector3 vector)
|
|
65
|
-
{
|
|
66
|
-
return $"{{{vector.x}, {vector.y}, {vector.z}}}";
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
public static string ToJsonString(this Vector2 vector)
|
|
70
|
-
{
|
|
71
|
-
return $"{{{vector.x}, {vector.y}}}";
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
public static bool IsNoise(this Vector2 inputVector)
|
|
75
|
-
{
|
|
76
|
-
return Mathf.Abs(inputVector.x) <= 0.2f && Mathf.Abs(inputVector.y) <= 0.2f;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
public static void Stop(this Rigidbody2D rigidBody)
|
|
80
|
-
{
|
|
81
|
-
if (rigidBody == null)
|
|
82
|
-
{
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
rigidBody.velocity = Vector2.zero;
|
|
87
|
-
rigidBody.angularVelocity = 0;
|
|
88
|
-
rigidBody.Sleep();
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
public static BoundsInt ExpandBounds(this BoundsInt source, BoundsInt other)
|
|
92
|
-
{
|
|
93
|
-
int xMin = Math.Min(source.xMin, other.xMin);
|
|
94
|
-
int xMax = Math.Max(source.xMax, other.xMax);
|
|
95
|
-
int yMin = Math.Min(source.yMin, other.yMin);
|
|
96
|
-
int yMax = Math.Max(source.yMax, other.yMax);
|
|
97
|
-
int zMin = Math.Min(source.zMin, other.zMin);
|
|
98
|
-
int zMax = Math.Max(source.zMax, other.zMax);
|
|
99
|
-
return new BoundsInt(xMin, yMin, zMin, xMax - xMin, yMax - yMin, zMax - zMin);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
public static BoundsInt? GetBounds(this IEnumerable<Vector3Int> positions)
|
|
103
|
-
{
|
|
104
|
-
bool any = false;
|
|
105
|
-
int xMin = int.MaxValue;
|
|
106
|
-
int xMax = int.MinValue;
|
|
107
|
-
int yMin = int.MaxValue;
|
|
108
|
-
int yMax = int.MinValue;
|
|
109
|
-
int zMin = int.MaxValue;
|
|
110
|
-
int zMax = int.MinValue;
|
|
111
|
-
foreach (Vector3Int position in positions)
|
|
112
|
-
{
|
|
113
|
-
any = true;
|
|
114
|
-
xMin = Math.Min(xMin, position.x);
|
|
115
|
-
xMax = Math.Max(xMax, position.x);
|
|
116
|
-
yMin = Math.Min(yMin, position.y);
|
|
117
|
-
yMax = Math.Max(yMax, position.y);
|
|
118
|
-
zMin = Math.Min(zMin, position.z);
|
|
119
|
-
zMax = Math.Max(zMax, position.z);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (!any)
|
|
123
|
-
{
|
|
124
|
-
return null;
|
|
125
|
-
}
|
|
126
|
-
return new BoundsInt(
|
|
127
|
-
xMin,
|
|
128
|
-
yMin,
|
|
129
|
-
zMin,
|
|
130
|
-
(xMax - xMin) + 1,
|
|
131
|
-
(yMax - yMin) + 1,
|
|
132
|
-
(zMax - zMin) + 1
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
public static BoundsInt? GetBounds(this IEnumerable<FastVector3Int> positions)
|
|
137
|
-
{
|
|
138
|
-
bool any = false;
|
|
139
|
-
int xMin = int.MaxValue;
|
|
140
|
-
int xMax = int.MinValue;
|
|
141
|
-
int yMin = int.MaxValue;
|
|
142
|
-
int yMax = int.MinValue;
|
|
143
|
-
int zMin = int.MaxValue;
|
|
144
|
-
int zMax = int.MinValue;
|
|
145
|
-
foreach (FastVector3Int position in positions)
|
|
146
|
-
{
|
|
147
|
-
any = true;
|
|
148
|
-
xMin = Math.Min(xMin, position.x);
|
|
149
|
-
xMax = Math.Max(xMax, position.x);
|
|
150
|
-
yMin = Math.Min(yMin, position.y);
|
|
151
|
-
yMax = Math.Max(yMax, position.y);
|
|
152
|
-
zMin = Math.Min(zMin, position.z);
|
|
153
|
-
zMax = Math.Max(zMax, position.z);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
if (!any)
|
|
157
|
-
{
|
|
158
|
-
return null;
|
|
159
|
-
}
|
|
160
|
-
return new BoundsInt(
|
|
161
|
-
xMin,
|
|
162
|
-
yMin,
|
|
163
|
-
zMin,
|
|
164
|
-
(xMax - xMin) + 1,
|
|
165
|
-
(yMax - yMin) + 1,
|
|
166
|
-
(zMax - zMin) + 1
|
|
167
|
-
);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
public static Bounds? GetBounds(this IEnumerable<Vector2> positions)
|
|
171
|
-
{
|
|
172
|
-
bool any = false;
|
|
173
|
-
float xMin = float.MaxValue;
|
|
174
|
-
float xMax = float.MinValue;
|
|
175
|
-
float yMin = float.MaxValue;
|
|
176
|
-
float yMax = float.MinValue;
|
|
177
|
-
foreach (Vector2 position in positions)
|
|
178
|
-
{
|
|
179
|
-
any = true;
|
|
180
|
-
xMin = Math.Min(xMin, position.x);
|
|
181
|
-
xMax = Math.Max(xMax, position.x);
|
|
182
|
-
yMin = Math.Min(yMin, position.y);
|
|
183
|
-
yMax = Math.Max(yMax, position.y);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
if (!any)
|
|
187
|
-
{
|
|
188
|
-
return null;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
Vector3 center = new((xMax + xMin) / 2f, (yMax + yMin) / 2f);
|
|
192
|
-
Vector3 size = new(xMax - xMin, yMax - yMin);
|
|
193
|
-
return new Bounds(center, size);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
public static Bounds? GetBounds(this IEnumerable<Bounds> boundaries)
|
|
197
|
-
{
|
|
198
|
-
float minX = float.MaxValue;
|
|
199
|
-
float minY = float.MaxValue;
|
|
200
|
-
float maxX = float.MinValue;
|
|
201
|
-
float maxY = float.MinValue;
|
|
202
|
-
bool any = false;
|
|
203
|
-
foreach (Bounds boundary in boundaries)
|
|
204
|
-
{
|
|
205
|
-
any = true;
|
|
206
|
-
Vector3 min = boundary.min;
|
|
207
|
-
Vector3 max = boundary.max;
|
|
208
|
-
minX = Math.Min(minX, min.x);
|
|
209
|
-
maxX = Math.Max(maxX, max.x);
|
|
210
|
-
minY = Math.Min(minY, min.y);
|
|
211
|
-
maxY = Math.Max(maxY, max.y);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
if (!any)
|
|
215
|
-
{
|
|
216
|
-
return null;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
return new Bounds(
|
|
220
|
-
new Vector3(minX + (maxX - minX) / 2, minY + (maxY - minY) / 2),
|
|
221
|
-
new Vector3(maxX - minX, maxY - minY)
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// https://www.habrador.com/tutorials/math/8-convex-hull/
|
|
226
|
-
public static List<Vector3Int> BuildConvexHull(
|
|
227
|
-
this IEnumerable<Vector3Int> pointsSet,
|
|
228
|
-
Grid grid,
|
|
229
|
-
IRandom random = null,
|
|
230
|
-
bool includeColinearPoints = true
|
|
231
|
-
)
|
|
232
|
-
{
|
|
233
|
-
List<Vector3Int> points = pointsSet.ToList();
|
|
234
|
-
if (points.Count <= 3)
|
|
235
|
-
{
|
|
236
|
-
return points;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
random ??= PRNG.Instance;
|
|
240
|
-
|
|
241
|
-
Vector2 CellToWorld(Vector3Int position) => grid.CellToWorld(position);
|
|
242
|
-
|
|
243
|
-
Vector3Int startPoint = points[0];
|
|
244
|
-
Vector2 startPointWorldPosition = CellToWorld(startPoint);
|
|
245
|
-
for (int i = 1; i < points.Count; ++i)
|
|
246
|
-
{
|
|
247
|
-
Vector3Int testPoint = points[i];
|
|
248
|
-
Vector2 testPointWorldPosition = CellToWorld(testPoint);
|
|
249
|
-
// ReSharper disable once CompareOfFloatsByEqualityOperator
|
|
250
|
-
if (
|
|
251
|
-
testPointWorldPosition.x < startPointWorldPosition.x
|
|
252
|
-
|| (
|
|
253
|
-
Mathf.Approximately(testPointWorldPosition.x, startPointWorldPosition.x)
|
|
254
|
-
&& testPointWorldPosition.y < startPointWorldPosition.y
|
|
255
|
-
)
|
|
256
|
-
)
|
|
257
|
-
{
|
|
258
|
-
startPoint = testPoint;
|
|
259
|
-
startPointWorldPosition = testPointWorldPosition;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
List<Vector3Int> convexHull = new() { startPoint };
|
|
264
|
-
_ = points.Remove(startPoint);
|
|
265
|
-
Vector3Int currentPoint = convexHull[0];
|
|
266
|
-
List<Vector3Int> colinearPoints = new();
|
|
267
|
-
int counter = 0;
|
|
268
|
-
while (true)
|
|
269
|
-
{
|
|
270
|
-
if (counter == 2)
|
|
271
|
-
{
|
|
272
|
-
points.Add(convexHull[0]);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
if (points.Count <= 0)
|
|
276
|
-
{
|
|
277
|
-
return convexHull;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
Vector3Int nextPoint = random.NextOf(points);
|
|
281
|
-
Vector2 currentPointWorldPosition = CellToWorld(currentPoint);
|
|
282
|
-
Vector2 nextPointWorldPosition = CellToWorld(nextPoint);
|
|
283
|
-
foreach (Vector3Int point in points)
|
|
284
|
-
{
|
|
285
|
-
if (Equals(point, nextPoint))
|
|
286
|
-
{
|
|
287
|
-
continue;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
float relation = Geometry.IsAPointLeftOfVectorOrOnTheLine(
|
|
291
|
-
currentPointWorldPosition,
|
|
292
|
-
nextPointWorldPosition,
|
|
293
|
-
CellToWorld(point)
|
|
294
|
-
);
|
|
295
|
-
if (Mathf.Approximately(relation, 0))
|
|
296
|
-
{
|
|
297
|
-
colinearPoints.Add(point);
|
|
298
|
-
}
|
|
299
|
-
else if (relation < 0)
|
|
300
|
-
{
|
|
301
|
-
nextPoint = point;
|
|
302
|
-
nextPointWorldPosition = CellToWorld(nextPoint);
|
|
303
|
-
colinearPoints.Clear();
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
if (0 < colinearPoints.Count)
|
|
308
|
-
{
|
|
309
|
-
colinearPoints.Add(nextPoint);
|
|
310
|
-
colinearPoints.Sort(
|
|
311
|
-
(lhs, rhs) =>
|
|
312
|
-
(CellToWorld(lhs) - currentPointWorldPosition).sqrMagnitude.CompareTo(
|
|
313
|
-
(CellToWorld(rhs) - currentPointWorldPosition).sqrMagnitude
|
|
314
|
-
)
|
|
315
|
-
);
|
|
316
|
-
|
|
317
|
-
if (includeColinearPoints)
|
|
318
|
-
{
|
|
319
|
-
convexHull.AddRange(colinearPoints);
|
|
320
|
-
}
|
|
321
|
-
else
|
|
322
|
-
{
|
|
323
|
-
convexHull.Add(colinearPoints[^1]);
|
|
324
|
-
_ = points.Remove(colinearPoints[^1]);
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
currentPoint = colinearPoints[^1];
|
|
328
|
-
_ = points.RemoveAll(colinearPoints.Contains);
|
|
329
|
-
colinearPoints.Clear();
|
|
330
|
-
}
|
|
331
|
-
else
|
|
332
|
-
{
|
|
333
|
-
convexHull.Add(nextPoint);
|
|
334
|
-
_ = points.Remove(nextPoint);
|
|
335
|
-
currentPoint = nextPoint;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
if (Equals(currentPoint, convexHull[0]))
|
|
339
|
-
{
|
|
340
|
-
convexHull.RemoveAt(convexHull.Count - 1);
|
|
341
|
-
break;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
++counter;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
return convexHull;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
public static List<FastVector3Int> BuildConvexHull(
|
|
351
|
-
this IEnumerable<FastVector3Int> pointsSet,
|
|
352
|
-
Grid grid,
|
|
353
|
-
IRandom random = null,
|
|
354
|
-
bool includeColinearPoints = false
|
|
355
|
-
)
|
|
356
|
-
{
|
|
357
|
-
List<FastVector3Int> points = pointsSet.ToList();
|
|
358
|
-
if (points.Count <= 3)
|
|
359
|
-
{
|
|
360
|
-
return points;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
random ??= PRNG.Instance;
|
|
364
|
-
|
|
365
|
-
Vector2 CellToWorld(FastVector3Int position) => grid.CellToWorld(position);
|
|
366
|
-
|
|
367
|
-
FastVector3Int startPoint = points[0];
|
|
368
|
-
Vector2 startPointWorldPosition = CellToWorld(startPoint);
|
|
369
|
-
for (int i = 1; i < points.Count; ++i)
|
|
370
|
-
{
|
|
371
|
-
FastVector3Int testPoint = points[i];
|
|
372
|
-
Vector2 testPointWorldPosition = CellToWorld(testPoint);
|
|
373
|
-
if (
|
|
374
|
-
testPointWorldPosition.x < startPointWorldPosition.x
|
|
375
|
-
|| (
|
|
376
|
-
Mathf.Approximately(testPointWorldPosition.x, startPointWorldPosition.x)
|
|
377
|
-
&& testPointWorldPosition.y < startPointWorldPosition.y
|
|
378
|
-
)
|
|
379
|
-
)
|
|
380
|
-
{
|
|
381
|
-
startPoint = testPoint;
|
|
382
|
-
startPointWorldPosition = testPointWorldPosition;
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
List<FastVector3Int> convexHull = new() { startPoint };
|
|
387
|
-
_ = points.Remove(startPoint);
|
|
388
|
-
FastVector3Int currentPoint = convexHull[0];
|
|
389
|
-
List<FastVector3Int> colinearPoints = new();
|
|
390
|
-
int counter = 0;
|
|
391
|
-
while (true)
|
|
392
|
-
{
|
|
393
|
-
if (counter == 2)
|
|
394
|
-
{
|
|
395
|
-
points.Add(convexHull[0]);
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
if (points.Count <= 0)
|
|
399
|
-
{
|
|
400
|
-
return convexHull;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
FastVector3Int nextPoint = random.NextOf(points);
|
|
404
|
-
Vector2 currentPointWorldPosition = CellToWorld(currentPoint);
|
|
405
|
-
Vector2 nextPointWorldPosition = CellToWorld(nextPoint);
|
|
406
|
-
foreach (FastVector3Int point in points)
|
|
407
|
-
{
|
|
408
|
-
if (point.Equals(nextPoint))
|
|
409
|
-
{
|
|
410
|
-
continue;
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
float relation = Geometry.IsAPointLeftOfVectorOrOnTheLine(
|
|
414
|
-
currentPointWorldPosition,
|
|
415
|
-
nextPointWorldPosition,
|
|
416
|
-
CellToWorld(point)
|
|
417
|
-
);
|
|
418
|
-
if (Mathf.Approximately(relation, 0))
|
|
419
|
-
{
|
|
420
|
-
colinearPoints.Add(point);
|
|
421
|
-
}
|
|
422
|
-
else if (relation < 0)
|
|
423
|
-
{
|
|
424
|
-
nextPoint = point;
|
|
425
|
-
nextPointWorldPosition = CellToWorld(nextPoint);
|
|
426
|
-
colinearPoints.Clear();
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
if (0 < colinearPoints.Count)
|
|
431
|
-
{
|
|
432
|
-
colinearPoints.Add(nextPoint);
|
|
433
|
-
colinearPoints.Sort(
|
|
434
|
-
(lhs, rhs) =>
|
|
435
|
-
(CellToWorld(lhs) - currentPointWorldPosition).sqrMagnitude.CompareTo(
|
|
436
|
-
(CellToWorld(rhs) - currentPointWorldPosition).sqrMagnitude
|
|
437
|
-
)
|
|
438
|
-
);
|
|
439
|
-
|
|
440
|
-
if (includeColinearPoints)
|
|
441
|
-
{
|
|
442
|
-
convexHull.AddRange(colinearPoints);
|
|
443
|
-
}
|
|
444
|
-
else
|
|
445
|
-
{
|
|
446
|
-
convexHull.Add(colinearPoints[^1]);
|
|
447
|
-
_ = points.Remove(colinearPoints[^1]);
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
currentPoint = colinearPoints[^1];
|
|
451
|
-
_ = points.RemoveAll(colinearPoints.Contains);
|
|
452
|
-
colinearPoints.Clear();
|
|
453
|
-
}
|
|
454
|
-
else
|
|
455
|
-
{
|
|
456
|
-
convexHull.Add(nextPoint);
|
|
457
|
-
_ = points.Remove(nextPoint);
|
|
458
|
-
currentPoint = nextPoint;
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
if (currentPoint.Equals(convexHull[0]))
|
|
462
|
-
{
|
|
463
|
-
convexHull.RemoveAt(convexHull.Count - 1);
|
|
464
|
-
break;
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
++counter;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
return convexHull;
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
public static bool IsConvexHullInsideConvexHull(
|
|
474
|
-
this List<FastVector3Int> convexHull,
|
|
475
|
-
Grid grid,
|
|
476
|
-
List<FastVector3Int> maybeInside
|
|
477
|
-
)
|
|
478
|
-
{
|
|
479
|
-
foreach (FastVector3Int point in maybeInside)
|
|
480
|
-
{
|
|
481
|
-
if (!IsPointInsideConvexHull(convexHull, grid, point))
|
|
482
|
-
{
|
|
483
|
-
return false;
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
return true;
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
public static bool IsPointInsideConvexHull(
|
|
491
|
-
this List<Vector3Int> convexHull,
|
|
492
|
-
Grid grid,
|
|
493
|
-
Vector3Int point
|
|
494
|
-
)
|
|
495
|
-
{
|
|
496
|
-
for (int i = 0; i < convexHull.Count; ++i)
|
|
497
|
-
{
|
|
498
|
-
Vector3Int lhs = convexHull[i];
|
|
499
|
-
Vector3Int rhs = convexHull[(i + 1) % convexHull.Count];
|
|
500
|
-
float relation = Geometry.IsAPointLeftOfVectorOrOnTheLine(
|
|
501
|
-
grid.CellToWorld(lhs),
|
|
502
|
-
grid.CellToWorld(rhs),
|
|
503
|
-
grid.CellToWorld(point)
|
|
504
|
-
);
|
|
505
|
-
if (relation < 0)
|
|
506
|
-
{
|
|
507
|
-
return false;
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
return true;
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
public static bool IsPointInsideConvexHull(
|
|
514
|
-
this List<FastVector3Int> convexHull,
|
|
515
|
-
Grid grid,
|
|
516
|
-
FastVector3Int point
|
|
517
|
-
)
|
|
518
|
-
{
|
|
519
|
-
for (int i = 0; i < convexHull.Count; ++i)
|
|
520
|
-
{
|
|
521
|
-
FastVector3Int lhs = convexHull[i];
|
|
522
|
-
FastVector3Int rhs = convexHull[(i + 1) % convexHull.Count];
|
|
523
|
-
float relation = Geometry.IsAPointLeftOfVectorOrOnTheLine(
|
|
524
|
-
grid.CellToWorld(lhs),
|
|
525
|
-
grid.CellToWorld(rhs),
|
|
526
|
-
grid.CellToWorld(point)
|
|
527
|
-
);
|
|
528
|
-
if (relation < 0)
|
|
529
|
-
{
|
|
530
|
-
return false;
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
return true;
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
public static bool IsConvexHullInsideConvexHull(
|
|
537
|
-
this List<Vector3Int> convexHull,
|
|
538
|
-
Grid grid,
|
|
539
|
-
List<Vector3Int> maybeInside
|
|
540
|
-
)
|
|
541
|
-
{
|
|
542
|
-
foreach (Vector3Int point in maybeInside)
|
|
543
|
-
{
|
|
544
|
-
if (!IsPointInsideConvexHull(convexHull, grid, point))
|
|
545
|
-
{
|
|
546
|
-
return false;
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
return true;
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
private readonly struct HullEdge
|
|
554
|
-
{
|
|
555
|
-
public readonly float edgeLength;
|
|
556
|
-
|
|
557
|
-
public readonly FastVector3Int from;
|
|
558
|
-
public readonly FastVector3Int to;
|
|
559
|
-
|
|
560
|
-
public readonly Vector2 fromWorld;
|
|
561
|
-
public readonly Vector2 toWorld;
|
|
562
|
-
|
|
563
|
-
private readonly Grid _grid;
|
|
564
|
-
|
|
565
|
-
public HullEdge(FastVector3Int from, FastVector3Int to, Grid grid)
|
|
566
|
-
{
|
|
567
|
-
this.from = from;
|
|
568
|
-
this.to = to;
|
|
569
|
-
_grid = grid;
|
|
570
|
-
fromWorld = grid.CellToWorld(from);
|
|
571
|
-
toWorld = grid.CellToWorld(to);
|
|
572
|
-
edgeLength = (fromWorld - toWorld).sqrMagnitude;
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
public bool Intersects(HullEdge other)
|
|
576
|
-
{
|
|
577
|
-
return UnityExtensions.Intersects(
|
|
578
|
-
fromWorld,
|
|
579
|
-
toWorld,
|
|
580
|
-
other.fromWorld,
|
|
581
|
-
other.toWorld
|
|
582
|
-
);
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
public float LargestAngle(FastVector3Int point)
|
|
586
|
-
{
|
|
587
|
-
Vector2 worldPoint = _grid.CellToWorld(point);
|
|
588
|
-
float angleFrom = Vector2.Angle((toWorld - fromWorld), (worldPoint - fromWorld));
|
|
589
|
-
float angleTo = Vector2.Angle((fromWorld - toWorld), (worldPoint - toWorld));
|
|
590
|
-
return Math.Max(angleFrom, angleTo);
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
private sealed class ConcaveHullComparer : IComparer<HullEdge>
|
|
595
|
-
{
|
|
596
|
-
public static readonly ConcaveHullComparer Instance = new();
|
|
597
|
-
|
|
598
|
-
private ConcaveHullComparer() { }
|
|
599
|
-
|
|
600
|
-
public int Compare(HullEdge lhs, HullEdge rhs)
|
|
601
|
-
{
|
|
602
|
-
int comparison = lhs.edgeLength.CompareTo(rhs.edgeLength);
|
|
603
|
-
if (comparison != 0)
|
|
604
|
-
{
|
|
605
|
-
return comparison;
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
comparison = lhs.from.CompareTo(rhs.from);
|
|
609
|
-
if (comparison != 0)
|
|
610
|
-
{
|
|
611
|
-
return comparison;
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
return lhs.to.CompareTo(rhs.to);
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
public static List<FastVector3Int> BuildConcaveHull3(
|
|
619
|
-
this IReadOnlyCollection<FastVector3Int> gridPositions,
|
|
620
|
-
Grid grid,
|
|
621
|
-
IRandom random = null,
|
|
622
|
-
int bucketSize = 40,
|
|
623
|
-
float angleThreshold = 90f
|
|
624
|
-
)
|
|
625
|
-
{
|
|
626
|
-
List<FastVector3Int> convexHull = gridPositions.BuildConvexHull(grid, random);
|
|
627
|
-
List<HullEdge> concaveHullEdges = new();
|
|
628
|
-
|
|
629
|
-
SortedSet<HullEdge> data = new(ConcaveHullComparer.Instance);
|
|
630
|
-
for (int i = 0; i < convexHull.Count; ++i)
|
|
631
|
-
{
|
|
632
|
-
FastVector3Int lhs = convexHull[i];
|
|
633
|
-
FastVector3Int rhs = convexHull[(i + 1) % convexHull.Count];
|
|
634
|
-
HullEdge edge = new(lhs, rhs, grid);
|
|
635
|
-
_ = data.Add(edge);
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
HashSet<FastVector3Int> remainingPoints = gridPositions.ToHashSet();
|
|
639
|
-
remainingPoints.ExceptWith(convexHull);
|
|
640
|
-
|
|
641
|
-
Vector2 CellToWorld(FastVector3Int cell) => grid.CellToWorld(cell);
|
|
642
|
-
|
|
643
|
-
Bounds? maybeBounds = gridPositions.Select(CellToWorld).GetBounds();
|
|
644
|
-
if (maybeBounds == null)
|
|
645
|
-
{
|
|
646
|
-
throw new ArgumentException(nameof(gridPositions));
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
QuadTree<FastVector3Int> NewQuadTree() =>
|
|
650
|
-
new(gridPositions, CellToWorld, maybeBounds.Value, bucketSize: bucketSize);
|
|
651
|
-
|
|
652
|
-
QuadTree<FastVector3Int> quadTree = NewQuadTree();
|
|
653
|
-
List<FastVector3Int> neighbors = Buffers<FastVector3Int>.List;
|
|
654
|
-
while (0 < data.Count)
|
|
655
|
-
{
|
|
656
|
-
HullEdge edge = data.Max;
|
|
657
|
-
_ = data.Remove(edge);
|
|
658
|
-
|
|
659
|
-
Vector2 edgeCenter = edge.fromWorld + (edge.toWorld - edge.fromWorld) / 2;
|
|
660
|
-
quadTree.GetApproximateNearestNeighbors(edgeCenter, bucketSize, neighbors);
|
|
661
|
-
float localMaximumDistance = float.MinValue;
|
|
662
|
-
foreach (FastVector3Int neighbor in neighbors)
|
|
663
|
-
{
|
|
664
|
-
if (neighbor == edge.to || neighbor == edge.from)
|
|
665
|
-
{
|
|
666
|
-
continue;
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
localMaximumDistance = Math.Max(
|
|
670
|
-
localMaximumDistance,
|
|
671
|
-
(CellToWorld(neighbor) - edgeCenter).sqrMagnitude
|
|
672
|
-
);
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
if (edge.edgeLength <= localMaximumDistance)
|
|
676
|
-
{
|
|
677
|
-
concaveHullEdges.Add(edge);
|
|
678
|
-
continue;
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
float smallestAngle = float.MaxValue;
|
|
682
|
-
FastVector3Int? maybeChosenPoint = null;
|
|
683
|
-
foreach (FastVector3Int remainingPoint in remainingPoints)
|
|
684
|
-
{
|
|
685
|
-
float maximumAngle = edge.LargestAngle(remainingPoint);
|
|
686
|
-
if (maximumAngle < smallestAngle)
|
|
687
|
-
{
|
|
688
|
-
maybeChosenPoint = remainingPoint;
|
|
689
|
-
smallestAngle = maximumAngle;
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
if (angleThreshold < smallestAngle)
|
|
694
|
-
{
|
|
695
|
-
concaveHullEdges.Add(edge);
|
|
696
|
-
continue;
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
if (maybeChosenPoint == null)
|
|
700
|
-
{
|
|
701
|
-
concaveHullEdges.Add(edge);
|
|
702
|
-
continue;
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
FastVector3Int chosenPoint = maybeChosenPoint.Value;
|
|
706
|
-
HullEdge e2 = new(edge.from, chosenPoint, grid);
|
|
707
|
-
HullEdge e3 = new(chosenPoint, edge.to, grid);
|
|
708
|
-
bool intersects = false;
|
|
709
|
-
foreach (HullEdge convexHullEdge in data)
|
|
710
|
-
{
|
|
711
|
-
if (convexHullEdge.Intersects(e2))
|
|
712
|
-
{
|
|
713
|
-
intersects = true;
|
|
714
|
-
break;
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
if (convexHullEdge.Intersects(e3))
|
|
718
|
-
{
|
|
719
|
-
intersects = true;
|
|
720
|
-
break;
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
if (!intersects)
|
|
725
|
-
{
|
|
726
|
-
foreach (HullEdge concaveHullEdge in concaveHullEdges)
|
|
727
|
-
{
|
|
728
|
-
if (concaveHullEdge.Intersects(e2))
|
|
729
|
-
{
|
|
730
|
-
intersects = true;
|
|
731
|
-
break;
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
if (concaveHullEdge.Intersects(e3))
|
|
735
|
-
{
|
|
736
|
-
intersects = true;
|
|
737
|
-
break;
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
if (!intersects)
|
|
743
|
-
{
|
|
744
|
-
_ = data.Add(e2);
|
|
745
|
-
_ = data.Add(e3);
|
|
746
|
-
_ = remainingPoints.Remove(maybeChosenPoint.Value);
|
|
747
|
-
}
|
|
748
|
-
else
|
|
749
|
-
{
|
|
750
|
-
concaveHullEdges.Add(edge);
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
List<FastVector3Int> concaveHull = new(concaveHullEdges.Count);
|
|
755
|
-
HullEdge current = concaveHullEdges[0];
|
|
756
|
-
concaveHullEdges.RemoveAtSwapBack(0);
|
|
757
|
-
concaveHull.Add(current.from);
|
|
758
|
-
while (0 < concaveHullEdges.Count)
|
|
759
|
-
{
|
|
760
|
-
FastVector3Int to = current.to;
|
|
761
|
-
int nextIndex = concaveHullEdges.FindIndex(edge => edge.from == to);
|
|
762
|
-
current = concaveHullEdges[nextIndex];
|
|
763
|
-
concaveHullEdges.RemoveAtSwapBack(nextIndex);
|
|
764
|
-
concaveHull.Add(current.from);
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
return concaveHull;
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
// https://www.researchgate.net/publication/220868874_Concave_hull_A_k-nearest_neighbours_approach_for_the_computation_of_the_region_occupied_by_a_set_of_points
|
|
771
|
-
|
|
772
|
-
public static List<FastVector3Int> BuildConcaveHull2(
|
|
773
|
-
this IReadOnlyCollection<FastVector3Int> gridPositions,
|
|
774
|
-
Grid grid,
|
|
775
|
-
IRandom random = null,
|
|
776
|
-
int nearestNeighbors = 3
|
|
777
|
-
)
|
|
778
|
-
{
|
|
779
|
-
const int minimumNearestNeighbors = 3;
|
|
780
|
-
nearestNeighbors = Math.Max(minimumNearestNeighbors, nearestNeighbors);
|
|
781
|
-
List<FastVector3Int> dataSet = gridPositions.Distinct().ToList();
|
|
782
|
-
if (dataSet.Count <= 3)
|
|
783
|
-
{
|
|
784
|
-
return dataSet;
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
nearestNeighbors = Math.Min(dataSet.Count, nearestNeighbors);
|
|
788
|
-
|
|
789
|
-
IComparer<FastVector3Int> comparison = Comparer<FastVector3Int>.Create(
|
|
790
|
-
(lhs, rhs) => grid.CellToWorld(lhs).y.CompareTo(grid.CellToWorld(rhs).y)
|
|
791
|
-
);
|
|
792
|
-
|
|
793
|
-
FastVector3Int? maybeFirst = null;
|
|
794
|
-
foreach (FastVector3Int gridPosition in dataSet)
|
|
795
|
-
{
|
|
796
|
-
if (maybeFirst == null || comparison.Compare(gridPosition, maybeFirst.Value) < 0)
|
|
797
|
-
{
|
|
798
|
-
maybeFirst = gridPosition;
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
if (maybeFirst == null)
|
|
803
|
-
{
|
|
804
|
-
return dataSet;
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
FastVector3Int first = maybeFirst.Value;
|
|
808
|
-
List<FastVector3Int> hull = new(dataSet.Count) { first };
|
|
809
|
-
int step = 2;
|
|
810
|
-
float previousAngle = 0f;
|
|
811
|
-
FastVector3Int current = first;
|
|
812
|
-
_ = dataSet.Remove(current);
|
|
813
|
-
|
|
814
|
-
float CalculateAngle(Vector2 lhs, Vector2 rhs)
|
|
815
|
-
{
|
|
816
|
-
return Mathf.Atan2(rhs.y - lhs.y, rhs.x - lhs.x);
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
// https://github.com/merowech/java-concave-hull/blob/master/ConcaveHull.java
|
|
820
|
-
float AngleDifference(float lhsAngle, float rhsAngle)
|
|
821
|
-
{
|
|
822
|
-
if (0 < lhsAngle && 0 <= rhsAngle && rhsAngle < lhsAngle)
|
|
823
|
-
{
|
|
824
|
-
return Math.Abs(lhsAngle - rhsAngle);
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
if (0 <= lhsAngle && 0 < rhsAngle && lhsAngle < rhsAngle)
|
|
828
|
-
{
|
|
829
|
-
return 2 * Mathf.PI + lhsAngle - rhsAngle;
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
if (lhsAngle < 0 && rhsAngle <= 0 && lhsAngle < rhsAngle)
|
|
833
|
-
{
|
|
834
|
-
return 2 * Mathf.PI + lhsAngle + Math.Abs(rhsAngle);
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
if (lhsAngle <= 0 && rhsAngle < 0 && rhsAngle < lhsAngle)
|
|
838
|
-
{
|
|
839
|
-
return Math.Abs(lhsAngle - rhsAngle);
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
if (lhsAngle <= 0 && 0 < rhsAngle)
|
|
843
|
-
{
|
|
844
|
-
return 2 * Mathf.PI + lhsAngle - rhsAngle;
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
if (0 <= lhsAngle && rhsAngle <= 0)
|
|
848
|
-
{
|
|
849
|
-
return lhsAngle + Math.Abs(rhsAngle);
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
return 0f;
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
// Order by descending right hand turns
|
|
856
|
-
int RightHandTurnComparison(FastVector3Int lhs, FastVector3Int rhs)
|
|
857
|
-
{
|
|
858
|
-
// TODO: I think this is fucked
|
|
859
|
-
Vector2 currentPoint = grid.CellToWorld(current);
|
|
860
|
-
Vector2 lhsPoint = grid.CellToWorld(lhs);
|
|
861
|
-
Vector2 rhsPoint = grid.CellToWorld(rhs);
|
|
862
|
-
|
|
863
|
-
float lhsAngle = AngleDifference(
|
|
864
|
-
previousAngle,
|
|
865
|
-
CalculateAngle(currentPoint, lhsPoint)
|
|
866
|
-
);
|
|
867
|
-
float rhsAngle = AngleDifference(
|
|
868
|
-
previousAngle,
|
|
869
|
-
CalculateAngle(currentPoint, rhsPoint)
|
|
870
|
-
);
|
|
871
|
-
return rhsAngle.CompareTo(lhsAngle);
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
List<FastVector3Int> clockwisePoints = Buffers<FastVector3Int>.List;
|
|
875
|
-
void FindNearestNeighborsAndPutInClockwisePoints()
|
|
876
|
-
{
|
|
877
|
-
clockwisePoints.Clear();
|
|
878
|
-
clockwisePoints.AddRange(dataSet);
|
|
879
|
-
Vector2 currentPoint = grid.CellToWorld(current);
|
|
880
|
-
clockwisePoints.Sort(
|
|
881
|
-
(lhs, rhs) =>
|
|
882
|
-
{
|
|
883
|
-
Vector2 lhsPoint = grid.CellToWorld(lhs);
|
|
884
|
-
Vector2 rhsPoint = grid.CellToWorld(rhs);
|
|
885
|
-
return (lhsPoint - currentPoint).sqrMagnitude.CompareTo(
|
|
886
|
-
(rhsPoint - currentPoint).sqrMagnitude
|
|
887
|
-
);
|
|
888
|
-
}
|
|
889
|
-
);
|
|
890
|
-
if (nearestNeighbors < clockwisePoints.Count)
|
|
891
|
-
{
|
|
892
|
-
clockwisePoints.RemoveRange(
|
|
893
|
-
nearestNeighbors,
|
|
894
|
-
clockwisePoints.Count - nearestNeighbors
|
|
895
|
-
);
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
while (0 < dataSet.Count)
|
|
900
|
-
{
|
|
901
|
-
if (step == 5)
|
|
902
|
-
{
|
|
903
|
-
dataSet.Add(first);
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
FindNearestNeighborsAndPutInClockwisePoints();
|
|
907
|
-
clockwisePoints.Sort(RightHandTurnComparison);
|
|
908
|
-
|
|
909
|
-
bool intersects = true;
|
|
910
|
-
int i = -1;
|
|
911
|
-
while (intersects && i < clockwisePoints.Count - 1)
|
|
912
|
-
{
|
|
913
|
-
++i;
|
|
914
|
-
|
|
915
|
-
FastVector3Int indexedPoint = clockwisePoints[i];
|
|
916
|
-
int lastPoint = indexedPoint == first ? 1 : 0;
|
|
917
|
-
int j = 2;
|
|
918
|
-
intersects = false;
|
|
919
|
-
Vector2 lhsTo = grid.CellToWorld(indexedPoint);
|
|
920
|
-
while (!intersects && j < hull.Count - lastPoint)
|
|
921
|
-
{
|
|
922
|
-
Vector2 lhsFrom = grid.CellToWorld(hull[step - 2]);
|
|
923
|
-
Vector2 rhsFrom = grid.CellToWorld(hull[step - 2 - j]);
|
|
924
|
-
Vector2 rhsTo = grid.CellToWorld(hull[step - 1 - j]);
|
|
925
|
-
intersects = Intersects(lhsFrom, lhsTo, rhsFrom, rhsTo);
|
|
926
|
-
++j;
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
if (intersects)
|
|
931
|
-
{
|
|
932
|
-
for (i = dataSet.Count - 1; 0 <= i; --i)
|
|
933
|
-
{
|
|
934
|
-
if (!IsPositionInside(hull, dataSet[i], grid))
|
|
935
|
-
{
|
|
936
|
-
return BuildConcaveHull2(
|
|
937
|
-
gridPositions,
|
|
938
|
-
grid,
|
|
939
|
-
random,
|
|
940
|
-
nearestNeighbors + 1
|
|
941
|
-
);
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
return hull;
|
|
946
|
-
}
|
|
947
|
-
|
|
948
|
-
current = clockwisePoints[i];
|
|
949
|
-
if (current != first)
|
|
950
|
-
{
|
|
951
|
-
hull.Add(current);
|
|
952
|
-
}
|
|
953
|
-
else
|
|
954
|
-
{
|
|
955
|
-
break;
|
|
956
|
-
}
|
|
957
|
-
|
|
958
|
-
int currentIndex = dataSet.IndexOf(current);
|
|
959
|
-
if (0 <= currentIndex)
|
|
960
|
-
{
|
|
961
|
-
dataSet.RemoveAtSwapBack(currentIndex);
|
|
962
|
-
}
|
|
963
|
-
|
|
964
|
-
previousAngle = CalculateAngle(
|
|
965
|
-
grid.CellToWorld(hull[step - 1]),
|
|
966
|
-
grid.CellToWorld(hull[step - 2])
|
|
967
|
-
);
|
|
968
|
-
++step;
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
for (int i = dataSet.Count - 1; 0 <= i; --i)
|
|
972
|
-
{
|
|
973
|
-
if (!IsPositionInside(hull, dataSet[i], grid))
|
|
974
|
-
{
|
|
975
|
-
return BuildConcaveHull2(gridPositions, grid, random, nearestNeighbors + 1);
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
return hull;
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
// This one has bugs, user beware
|
|
983
|
-
// https://github.com/Liagson/ConcaveHullGenerator/tree/master
|
|
984
|
-
#region ConcaveHull Functions
|
|
985
|
-
|
|
986
|
-
private readonly struct Line
|
|
987
|
-
{
|
|
988
|
-
public readonly double sqrMagnitude;
|
|
989
|
-
|
|
990
|
-
public readonly Vector2 from;
|
|
991
|
-
public readonly Vector2 to;
|
|
992
|
-
|
|
993
|
-
public Line(Vector2 from, Vector2 to)
|
|
994
|
-
{
|
|
995
|
-
this.from = from;
|
|
996
|
-
this.to = to;
|
|
997
|
-
sqrMagnitude = (from - to).sqrMagnitude;
|
|
998
|
-
}
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
public static List<FastVector3Int> BuildConcaveHull(
|
|
1002
|
-
this IEnumerable<FastVector3Int> gridPositions,
|
|
1003
|
-
Grid grid,
|
|
1004
|
-
IRandom random = null,
|
|
1005
|
-
float scaleFactor = 1,
|
|
1006
|
-
float concavity = 0f
|
|
1007
|
-
)
|
|
1008
|
-
{
|
|
1009
|
-
if (concavity < -1 || 1 < concavity)
|
|
1010
|
-
{
|
|
1011
|
-
throw new ArgumentException($"Concavity must be between [-1, 1], was {concavity}");
|
|
1012
|
-
}
|
|
1013
|
-
|
|
1014
|
-
List<FastVector3Int> originalGridPositions = gridPositions.ToList();
|
|
1015
|
-
if (originalGridPositions.Count <= 3)
|
|
1016
|
-
{
|
|
1017
|
-
return originalGridPositions;
|
|
1018
|
-
}
|
|
1019
|
-
|
|
1020
|
-
List<FastVector3Int> convexHull = originalGridPositions.BuildConvexHull(grid, random);
|
|
1021
|
-
HashSet<FastVector3Int> unusedNodes = originalGridPositions.ToHashSet();
|
|
1022
|
-
unusedNodes.ExceptWith(convexHull);
|
|
1023
|
-
List<Line> concaveHullLines = new(convexHull.Count);
|
|
1024
|
-
for (int i = 0; i < convexHull.Count; ++i)
|
|
1025
|
-
{
|
|
1026
|
-
FastVector3Int lhsGridPoint = convexHull[i];
|
|
1027
|
-
FastVector3Int rhsGridPoint = convexHull[(i + 1) % convexHull.Count];
|
|
1028
|
-
Vector2 lhs = grid.CellToWorld(lhsGridPoint);
|
|
1029
|
-
Vector2 rhs = grid.CellToWorld(rhsGridPoint);
|
|
1030
|
-
Line line = new(lhs, rhs);
|
|
1031
|
-
concaveHullLines.Add(line);
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
bool aLineWasDividedInTheIteration;
|
|
1035
|
-
do
|
|
1036
|
-
{
|
|
1037
|
-
// Order by descending
|
|
1038
|
-
concaveHullLines.Sort((lhs, rhs) => rhs.sqrMagnitude.CompareTo(lhs.sqrMagnitude));
|
|
1039
|
-
|
|
1040
|
-
aLineWasDividedInTheIteration = false;
|
|
1041
|
-
for (int i = 0; i < concaveHullLines.Count; ++i)
|
|
1042
|
-
{
|
|
1043
|
-
Line line = concaveHullLines[i];
|
|
1044
|
-
IEnumerable<FastVector3Int> nearbyPoints = GetNearbyPoints(
|
|
1045
|
-
line,
|
|
1046
|
-
unusedNodes,
|
|
1047
|
-
grid,
|
|
1048
|
-
scaleFactor
|
|
1049
|
-
);
|
|
1050
|
-
List<Line> dividedLine = GetDividedLine(
|
|
1051
|
-
line,
|
|
1052
|
-
nearbyPoints,
|
|
1053
|
-
concaveHullLines,
|
|
1054
|
-
grid,
|
|
1055
|
-
concavity
|
|
1056
|
-
);
|
|
1057
|
-
if (0 < dividedLine.Count)
|
|
1058
|
-
{
|
|
1059
|
-
aLineWasDividedInTheIteration = true;
|
|
1060
|
-
FastVector3Int toRemove = grid.WorldToCell(dividedLine[0].to);
|
|
1061
|
-
_ = unusedNodes.Remove(toRemove);
|
|
1062
|
-
concaveHullLines.AddRange(dividedLine);
|
|
1063
|
-
concaveHullLines.RemoveAtSwapBack(i);
|
|
1064
|
-
break;
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1067
|
-
} while (aLineWasDividedInTheIteration);
|
|
1068
|
-
|
|
1069
|
-
List<FastVector3Int> concaveHull = new(concaveHullLines.Count);
|
|
1070
|
-
if (concaveHullLines.Count <= 0)
|
|
1071
|
-
{
|
|
1072
|
-
return concaveHull;
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
Line currentlyConsideredLine = concaveHullLines[0];
|
|
1076
|
-
FastVector3Int from = grid.WorldToCell(currentlyConsideredLine.from);
|
|
1077
|
-
FastVector3Int to = grid.WorldToCell(currentlyConsideredLine.to);
|
|
1078
|
-
concaveHull.Add(from);
|
|
1079
|
-
concaveHull.Add(to);
|
|
1080
|
-
concaveHullLines.RemoveAtSwapBack(0);
|
|
1081
|
-
while (0 < concaveHullLines.Count)
|
|
1082
|
-
{
|
|
1083
|
-
int index = concaveHullLines.FindIndex(line =>
|
|
1084
|
-
{
|
|
1085
|
-
FastVector3Int lineFrom = grid.WorldToCell(line.from);
|
|
1086
|
-
return lineFrom == to;
|
|
1087
|
-
});
|
|
1088
|
-
|
|
1089
|
-
currentlyConsideredLine = concaveHullLines[index];
|
|
1090
|
-
to = grid.WorldToCell(currentlyConsideredLine.to);
|
|
1091
|
-
if (to == from)
|
|
1092
|
-
{
|
|
1093
|
-
break;
|
|
1094
|
-
}
|
|
1095
|
-
concaveHull.Add(to);
|
|
1096
|
-
concaveHullLines.RemoveAtSwapBack(index);
|
|
1097
|
-
}
|
|
1098
|
-
|
|
1099
|
-
return concaveHull;
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1102
|
-
public static bool IsPositionInside(
|
|
1103
|
-
List<FastVector3Int> hull,
|
|
1104
|
-
FastVector3Int gridPosition,
|
|
1105
|
-
Grid grid
|
|
1106
|
-
)
|
|
1107
|
-
{
|
|
1108
|
-
bool isPositionInside = false;
|
|
1109
|
-
Vector2 position = grid.CellToWorld(gridPosition);
|
|
1110
|
-
for (int i = 0; i < hull.Count; ++i)
|
|
1111
|
-
{
|
|
1112
|
-
Vector2 oldVector = grid.CellToWorld(hull[i]);
|
|
1113
|
-
int nextIndex = (i + 1) % hull.Count;
|
|
1114
|
-
Vector2 newVector = grid.CellToWorld(hull[nextIndex]);
|
|
1115
|
-
|
|
1116
|
-
Vector2 lhs;
|
|
1117
|
-
Vector2 rhs;
|
|
1118
|
-
if (oldVector.x < newVector.x)
|
|
1119
|
-
{
|
|
1120
|
-
lhs = oldVector;
|
|
1121
|
-
rhs = newVector;
|
|
1122
|
-
}
|
|
1123
|
-
else
|
|
1124
|
-
{
|
|
1125
|
-
lhs = newVector;
|
|
1126
|
-
rhs = oldVector;
|
|
1127
|
-
}
|
|
1128
|
-
|
|
1129
|
-
if (
|
|
1130
|
-
(newVector.x < position.x) == (position.x <= oldVector.x)
|
|
1131
|
-
&& (position.y - (long)lhs.y) * (rhs.x - lhs.x)
|
|
1132
|
-
< (rhs.y - (long)lhs.y) * (position.x - lhs.x)
|
|
1133
|
-
)
|
|
1134
|
-
{
|
|
1135
|
-
isPositionInside = !isPositionInside;
|
|
1136
|
-
}
|
|
1137
|
-
}
|
|
1138
|
-
|
|
1139
|
-
return isPositionInside;
|
|
1140
|
-
}
|
|
1141
|
-
|
|
1142
|
-
private static List<Line> GetDividedLine(
|
|
1143
|
-
Line line,
|
|
1144
|
-
IEnumerable<FastVector3Int> nearbyPoints,
|
|
1145
|
-
List<Line> concaveHull,
|
|
1146
|
-
Grid grid,
|
|
1147
|
-
float concavity
|
|
1148
|
-
)
|
|
1149
|
-
{
|
|
1150
|
-
return GetDividedLine(line.from, line.to, nearbyPoints, concaveHull, grid, concavity);
|
|
1151
|
-
}
|
|
1152
|
-
|
|
1153
|
-
private static List<Line> GetDividedLine(
|
|
1154
|
-
Vector2 from,
|
|
1155
|
-
Vector2 to,
|
|
1156
|
-
IEnumerable<FastVector3Int> nearbyPoints,
|
|
1157
|
-
List<Line> concaveHull,
|
|
1158
|
-
Grid grid,
|
|
1159
|
-
float concavity
|
|
1160
|
-
)
|
|
1161
|
-
{
|
|
1162
|
-
List<Line> dividedLine = new(2);
|
|
1163
|
-
Dictionary<Vector2, double> okMiddlePoints = new();
|
|
1164
|
-
foreach (FastVector3Int gridPoint in nearbyPoints)
|
|
1165
|
-
{
|
|
1166
|
-
Vector2 point = grid.CellToWorld(gridPoint);
|
|
1167
|
-
double cosine = GetCosine(from, to, point);
|
|
1168
|
-
if (cosine < concavity)
|
|
1169
|
-
{
|
|
1170
|
-
Line newLineA = new(from, point);
|
|
1171
|
-
Line newLineB = new(point, to);
|
|
1172
|
-
if (
|
|
1173
|
-
!LineCollidesWithHull(newLineA, concaveHull)
|
|
1174
|
-
&& !LineCollidesWithHull(newLineB, concaveHull)
|
|
1175
|
-
)
|
|
1176
|
-
{
|
|
1177
|
-
okMiddlePoints[point] = cosine;
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1180
|
-
}
|
|
1181
|
-
|
|
1182
|
-
if (0 < okMiddlePoints.Count)
|
|
1183
|
-
{
|
|
1184
|
-
Vector2 middlePoint = new();
|
|
1185
|
-
double minCosine = double.MaxValue;
|
|
1186
|
-
foreach (KeyValuePair<Vector2, double> entry in okMiddlePoints)
|
|
1187
|
-
{
|
|
1188
|
-
double cosine = entry.Value;
|
|
1189
|
-
if (cosine < minCosine)
|
|
1190
|
-
{
|
|
1191
|
-
minCosine = cosine;
|
|
1192
|
-
middlePoint = entry.Key;
|
|
1193
|
-
}
|
|
1194
|
-
}
|
|
1195
|
-
|
|
1196
|
-
dividedLine.Add(new Line(from, middlePoint));
|
|
1197
|
-
dividedLine.Add(new Line(middlePoint, to));
|
|
1198
|
-
}
|
|
1199
|
-
|
|
1200
|
-
return dividedLine;
|
|
1201
|
-
}
|
|
1202
|
-
|
|
1203
|
-
private static bool LineCollidesWithHull(Line line, List<Line> concaveHull)
|
|
1204
|
-
{
|
|
1205
|
-
return LineCollidesWithHull(line.from, line.to, concaveHull);
|
|
1206
|
-
}
|
|
1207
|
-
|
|
1208
|
-
private static bool LineCollidesWithHull(Vector2 from, Vector2 to, List<Line> concaveHull)
|
|
1209
|
-
{
|
|
1210
|
-
foreach (Line line in concaveHull)
|
|
1211
|
-
{
|
|
1212
|
-
Vector2 lhs = line.from;
|
|
1213
|
-
Vector2 rhs = line.to;
|
|
1214
|
-
|
|
1215
|
-
if (from != lhs && from != rhs && to != lhs && to != rhs)
|
|
1216
|
-
{
|
|
1217
|
-
if (Intersects(from, to, lhs, rhs))
|
|
1218
|
-
{
|
|
1219
|
-
return true;
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
}
|
|
1223
|
-
|
|
1224
|
-
return false;
|
|
1225
|
-
}
|
|
1226
|
-
|
|
1227
|
-
public static double GetCosine(Vector2 a, Vector3 b, Vector3 o)
|
|
1228
|
-
{
|
|
1229
|
-
/* Law of cosines */
|
|
1230
|
-
double aPow2 = (a.x - o.x) * (a.x - o.x) + (a.y - o.y) * (a.y - o.y);
|
|
1231
|
-
double bPow2 = (b.x - o.x) * (b.x - o.x) + (b.y - o.y) * (b.y - o.y);
|
|
1232
|
-
double cPow2 = (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
|
|
1233
|
-
double cos = (aPow2 + bPow2 - cPow2) / (2 * Math.Sqrt(aPow2 * bPow2));
|
|
1234
|
-
return Math.Round(cos, 4);
|
|
1235
|
-
}
|
|
1236
|
-
|
|
1237
|
-
private static IEnumerable<FastVector3Int> GetNearbyPoints(
|
|
1238
|
-
Line line,
|
|
1239
|
-
ICollection<FastVector3Int> points,
|
|
1240
|
-
Grid grid,
|
|
1241
|
-
float scaleFactor
|
|
1242
|
-
)
|
|
1243
|
-
{
|
|
1244
|
-
return GetNearbyPoints(line.from, line.to, points, grid, scaleFactor);
|
|
1245
|
-
}
|
|
1246
|
-
|
|
1247
|
-
private static IEnumerable<FastVector3Int> GetNearbyPoints(
|
|
1248
|
-
Vector2 from,
|
|
1249
|
-
Vector2 to,
|
|
1250
|
-
ICollection<FastVector3Int> points,
|
|
1251
|
-
Grid grid,
|
|
1252
|
-
float scaleFactor
|
|
1253
|
-
)
|
|
1254
|
-
{
|
|
1255
|
-
const int maxTries = 2;
|
|
1256
|
-
for (int tries = 0; tries < maxTries; ++tries)
|
|
1257
|
-
{
|
|
1258
|
-
bool foundAnyPoints = false;
|
|
1259
|
-
Bounds boundary = GetBoundary(from, to, scaleFactor);
|
|
1260
|
-
foreach (FastVector3Int gridPoint in points)
|
|
1261
|
-
{
|
|
1262
|
-
Vector2 point = grid.CellToWorld(gridPoint);
|
|
1263
|
-
if (point != from && point != to)
|
|
1264
|
-
{
|
|
1265
|
-
if (boundary.FastContains2D(point))
|
|
1266
|
-
{
|
|
1267
|
-
foundAnyPoints = true;
|
|
1268
|
-
yield return gridPoint;
|
|
1269
|
-
}
|
|
1270
|
-
}
|
|
1271
|
-
}
|
|
1272
|
-
|
|
1273
|
-
if (foundAnyPoints)
|
|
1274
|
-
{
|
|
1275
|
-
yield break;
|
|
1276
|
-
}
|
|
1277
|
-
|
|
1278
|
-
scaleFactor *= (4 / 3f);
|
|
1279
|
-
}
|
|
1280
|
-
}
|
|
1281
|
-
|
|
1282
|
-
private static Bounds GetBoundary(Vector2 from, Vector2 to, float scaleFactor)
|
|
1283
|
-
{
|
|
1284
|
-
float xMin = Math.Min(from.x, to.x);
|
|
1285
|
-
float yMin = Math.Min(from.y, to.y);
|
|
1286
|
-
float xMax = Math.Max(from.x, to.x);
|
|
1287
|
-
float yMax = Math.Max(from.y, to.y);
|
|
1288
|
-
|
|
1289
|
-
float width = xMax - xMin;
|
|
1290
|
-
float height = yMax - yMin;
|
|
1291
|
-
return new Bounds(
|
|
1292
|
-
new Vector3(xMin + width / 2, yMin + height / 2),
|
|
1293
|
-
new Vector3(width, height) * scaleFactor + new Vector3(0.001f, 0.001f)
|
|
1294
|
-
);
|
|
1295
|
-
}
|
|
1296
|
-
|
|
1297
|
-
// https://www.geeksforgeeks.org/how-to-check-if-a-given-point-lies-inside-a-polygon/#
|
|
1298
|
-
|
|
1299
|
-
/// <summary>
|
|
1300
|
-
/// Returns true if a line segment 'lhsFrom->lhsTo' intersects the line segment
|
|
1301
|
-
/// 'rhsFrom->rhsTo'
|
|
1302
|
-
/// </summary>
|
|
1303
|
-
/// <param name="lhsFrom">LineSegmentA start point.</param>
|
|
1304
|
-
/// <param name="lhsTo">LineSegmentA end point.</param>
|
|
1305
|
-
/// <param name="rhsFrom">LineSegmentB start point.</param>
|
|
1306
|
-
/// <param name="rhsTo">LineSegmentB end point.</param>
|
|
1307
|
-
/// <returns>True if the line segments intersect.</returns>
|
|
1308
|
-
public static bool Intersects(
|
|
1309
|
-
Vector2 lhsFrom,
|
|
1310
|
-
Vector2 lhsTo,
|
|
1311
|
-
Vector2 rhsFrom,
|
|
1312
|
-
Vector2 rhsTo
|
|
1313
|
-
)
|
|
1314
|
-
{
|
|
1315
|
-
if (lhsFrom == rhsFrom)
|
|
1316
|
-
{
|
|
1317
|
-
return false;
|
|
1318
|
-
}
|
|
1319
|
-
|
|
1320
|
-
if (lhsFrom == rhsTo)
|
|
1321
|
-
{
|
|
1322
|
-
return false;
|
|
1323
|
-
}
|
|
1324
|
-
|
|
1325
|
-
if (lhsTo == rhsFrom)
|
|
1326
|
-
{
|
|
1327
|
-
return false;
|
|
1328
|
-
}
|
|
1329
|
-
|
|
1330
|
-
if (lhsTo == rhsTo)
|
|
1331
|
-
{
|
|
1332
|
-
return false;
|
|
1333
|
-
}
|
|
1334
|
-
|
|
1335
|
-
OrientationType orientation1 = Orientation(lhsFrom, lhsTo, rhsFrom);
|
|
1336
|
-
OrientationType orientation2 = Orientation(lhsFrom, lhsTo, rhsTo);
|
|
1337
|
-
OrientationType orientation3 = Orientation(rhsFrom, rhsTo, lhsFrom);
|
|
1338
|
-
OrientationType orientation4 = Orientation(rhsFrom, rhsTo, lhsTo);
|
|
1339
|
-
|
|
1340
|
-
if (orientation1 != orientation2 && orientation3 != orientation4)
|
|
1341
|
-
{
|
|
1342
|
-
return true;
|
|
1343
|
-
}
|
|
1344
|
-
|
|
1345
|
-
if (orientation1 == OrientationType.Colinear && LiesOnSegment(lhsFrom, rhsFrom, lhsTo))
|
|
1346
|
-
{
|
|
1347
|
-
return true;
|
|
1348
|
-
}
|
|
1349
|
-
|
|
1350
|
-
if (orientation2 == OrientationType.Colinear && LiesOnSegment(lhsFrom, rhsTo, lhsTo))
|
|
1351
|
-
{
|
|
1352
|
-
return true;
|
|
1353
|
-
}
|
|
1354
|
-
|
|
1355
|
-
if (orientation3 == OrientationType.Colinear && LiesOnSegment(rhsFrom, lhsFrom, rhsTo))
|
|
1356
|
-
{
|
|
1357
|
-
return true;
|
|
1358
|
-
}
|
|
1359
|
-
|
|
1360
|
-
if (orientation4 == OrientationType.Colinear && LiesOnSegment(rhsFrom, lhsTo, rhsTo))
|
|
1361
|
-
{
|
|
1362
|
-
return true;
|
|
1363
|
-
}
|
|
1364
|
-
|
|
1365
|
-
return false;
|
|
1366
|
-
}
|
|
1367
|
-
|
|
1368
|
-
/// <summary>
|
|
1369
|
-
/// Given three colinear points p, q, r, returns whether the
|
|
1370
|
-
/// point q lines on the line segment pr.
|
|
1371
|
-
/// </summary>
|
|
1372
|
-
/// <param name="p">Beginning of line segment.</param>
|
|
1373
|
-
/// <param name="q">Check if on line segment.</param>
|
|
1374
|
-
/// <param name="r">End of line segment.</param>
|
|
1375
|
-
/// <returns>True if q lies on the line segment pr.</returns>
|
|
1376
|
-
public static bool LiesOnSegment(Vector2 p, Vector2 q, Vector2 r)
|
|
1377
|
-
{
|
|
1378
|
-
return q.x <= Math.Max(p.x, r.x)
|
|
1379
|
-
&& Math.Min(p.x, r.x) <= q.x
|
|
1380
|
-
&& q.y <= Math.Max(p.y, r.y)
|
|
1381
|
-
&& Math.Min(p.y, r.y) <= q.y;
|
|
1382
|
-
}
|
|
1383
|
-
|
|
1384
|
-
public enum OrientationType
|
|
1385
|
-
{
|
|
1386
|
-
Colinear = 0,
|
|
1387
|
-
Clockwise = 1,
|
|
1388
|
-
Counterclockwise = 2,
|
|
1389
|
-
}
|
|
1390
|
-
|
|
1391
|
-
/// <summary>
|
|
1392
|
-
/// Finds the orientation of an ordered triplet (p, q, r).
|
|
1393
|
-
/// </summary>
|
|
1394
|
-
/// <param name="p">Triplet element 1.</param>
|
|
1395
|
-
/// <param name="q">Triplet element 2.</param>
|
|
1396
|
-
/// <param name="r">Triplet element 3.</param>
|
|
1397
|
-
/// <returns>The orientation of the triplet</returns>
|
|
1398
|
-
public static OrientationType Orientation(Vector2 p, Vector2 q, Vector2 r)
|
|
1399
|
-
{
|
|
1400
|
-
float value = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
|
|
1401
|
-
if (Mathf.Approximately(value, 0))
|
|
1402
|
-
{
|
|
1403
|
-
return OrientationType.Colinear;
|
|
1404
|
-
}
|
|
1405
|
-
|
|
1406
|
-
return 0 < value ? OrientationType.Clockwise : OrientationType.Counterclockwise;
|
|
1407
|
-
}
|
|
1408
|
-
|
|
1409
|
-
#endregion
|
|
1410
|
-
|
|
1411
|
-
public static Vector2 Rotate(this Vector2 v, float degrees)
|
|
1412
|
-
{
|
|
1413
|
-
float sin = Mathf.Sin(degrees * Mathf.Deg2Rad);
|
|
1414
|
-
float cos = Mathf.Cos(degrees * Mathf.Deg2Rad);
|
|
1415
|
-
|
|
1416
|
-
float tx = v.x;
|
|
1417
|
-
float ty = v.y;
|
|
1418
|
-
|
|
1419
|
-
Vector2 rotatedVector;
|
|
1420
|
-
rotatedVector.x = (cos * tx) - (sin * ty);
|
|
1421
|
-
rotatedVector.y = (sin * tx) + (cos * ty);
|
|
1422
|
-
|
|
1423
|
-
return rotatedVector;
|
|
1424
|
-
}
|
|
1425
|
-
|
|
1426
|
-
public static bool FastIntersects(this Bounds bounds, Bounds other)
|
|
1427
|
-
{
|
|
1428
|
-
Vector3 boundsMin = bounds.min;
|
|
1429
|
-
Vector3 otherMax = other.max;
|
|
1430
|
-
if (otherMax.x < boundsMin.x || otherMax.y < boundsMin.y || otherMax.z < boundsMin.z)
|
|
1431
|
-
{
|
|
1432
|
-
return false;
|
|
1433
|
-
}
|
|
1434
|
-
|
|
1435
|
-
Vector3 boundsMax = bounds.max;
|
|
1436
|
-
Vector3 otherMin = other.min;
|
|
1437
|
-
return boundsMax.x >= otherMin.x
|
|
1438
|
-
&& boundsMax.y >= otherMin.y
|
|
1439
|
-
&& boundsMax.z >= otherMin.z;
|
|
1440
|
-
}
|
|
1441
|
-
|
|
1442
|
-
public static bool FastContains2D(this BoundsInt bounds, FastVector3Int position)
|
|
1443
|
-
{
|
|
1444
|
-
return position.x >= bounds.xMin
|
|
1445
|
-
&& position.y >= bounds.yMin
|
|
1446
|
-
&& position.x < bounds.xMax
|
|
1447
|
-
&& position.y < bounds.yMax;
|
|
1448
|
-
}
|
|
1449
|
-
|
|
1450
|
-
public static bool FastIntersects2D(this BoundsInt bounds, BoundsInt other)
|
|
1451
|
-
{
|
|
1452
|
-
if (other.xMax < bounds.xMin || other.yMax < bounds.yMin)
|
|
1453
|
-
{
|
|
1454
|
-
return false;
|
|
1455
|
-
}
|
|
1456
|
-
|
|
1457
|
-
return bounds.xMax >= other.xMin && bounds.yMax >= other.yMin;
|
|
1458
|
-
}
|
|
1459
|
-
|
|
1460
|
-
public static bool FastContains2D(this Bounds bounds, Vector2 position)
|
|
1461
|
-
{
|
|
1462
|
-
Vector3 min = bounds.min;
|
|
1463
|
-
if (position.x < min.x || position.y < bounds.min.y)
|
|
1464
|
-
{
|
|
1465
|
-
return false;
|
|
1466
|
-
}
|
|
1467
|
-
Vector3 max = bounds.max;
|
|
1468
|
-
return position.x < max.x && position.y < max.y;
|
|
1469
|
-
}
|
|
1470
|
-
|
|
1471
|
-
public static bool FastIntersects2D(this Bounds bounds, Bounds other)
|
|
1472
|
-
{
|
|
1473
|
-
Vector3 boundsMin = bounds.min;
|
|
1474
|
-
Vector3 otherMax = other.max;
|
|
1475
|
-
if (otherMax.x < boundsMin.x || otherMax.y < boundsMin.y)
|
|
1476
|
-
{
|
|
1477
|
-
return false;
|
|
1478
|
-
}
|
|
1479
|
-
|
|
1480
|
-
Vector3 boundsMax = bounds.max;
|
|
1481
|
-
Vector3 otherMin = other.min;
|
|
1482
|
-
return boundsMax.x >= otherMin.x && boundsMax.y >= otherMin.y;
|
|
1483
|
-
}
|
|
1484
|
-
|
|
1485
|
-
public static bool Overlaps2D(this Bounds bounds, Bounds other)
|
|
1486
|
-
{
|
|
1487
|
-
Vector3 boundsMin = bounds.min;
|
|
1488
|
-
Vector3 otherMin = other.min;
|
|
1489
|
-
if (otherMin.x < boundsMin.x || otherMin.y < boundsMin.y)
|
|
1490
|
-
{
|
|
1491
|
-
return false;
|
|
1492
|
-
}
|
|
1493
|
-
|
|
1494
|
-
Vector3 boundsMax = bounds.max;
|
|
1495
|
-
Vector3 otherMax = other.max;
|
|
1496
|
-
return otherMax.x <= boundsMax.x && otherMax.y <= boundsMax.y;
|
|
1497
|
-
}
|
|
1498
|
-
|
|
1499
|
-
public static BoundsInt WithPadding(this BoundsInt bounds, int xPadding, int yPadding)
|
|
1500
|
-
{
|
|
1501
|
-
Vector3Int size = bounds.size;
|
|
1502
|
-
return new BoundsInt(
|
|
1503
|
-
bounds.xMin - xPadding,
|
|
1504
|
-
bounds.yMin - yPadding,
|
|
1505
|
-
bounds.zMin,
|
|
1506
|
-
size.x + 2 * xPadding,
|
|
1507
|
-
size.y + 2 * yPadding,
|
|
1508
|
-
size.z
|
|
1509
|
-
);
|
|
1510
|
-
}
|
|
1511
|
-
|
|
1512
|
-
public static void SetColors(this UnityEngine.UI.Slider slider, Color color)
|
|
1513
|
-
{
|
|
1514
|
-
ColorBlock block = slider.colors;
|
|
1515
|
-
|
|
1516
|
-
block.normalColor = color;
|
|
1517
|
-
block.highlightedColor = color;
|
|
1518
|
-
block.pressedColor = color;
|
|
1519
|
-
block.selectedColor = color;
|
|
1520
|
-
block.disabledColor = color;
|
|
1521
|
-
|
|
1522
|
-
slider.colors = block;
|
|
1523
|
-
}
|
|
1524
|
-
|
|
1525
|
-
public static void SetLeft(this RectTransform rt, float left)
|
|
1526
|
-
{
|
|
1527
|
-
rt.offsetMin = new Vector2(left, rt.offsetMin.y);
|
|
1528
|
-
}
|
|
1529
|
-
|
|
1530
|
-
public static void SetRight(this RectTransform rt, float right)
|
|
1531
|
-
{
|
|
1532
|
-
rt.offsetMax = new Vector2(-right, rt.offsetMax.y);
|
|
1533
|
-
}
|
|
1534
|
-
|
|
1535
|
-
public static void SetTop(this RectTransform rt, float top)
|
|
1536
|
-
{
|
|
1537
|
-
rt.offsetMax = new Vector2(rt.offsetMax.x, -top);
|
|
1538
|
-
}
|
|
1539
|
-
|
|
1540
|
-
public static void SetBottom(this RectTransform rt, float bottom)
|
|
1541
|
-
{
|
|
1542
|
-
rt.offsetMin = new Vector2(rt.offsetMin.x, bottom);
|
|
1543
|
-
}
|
|
1544
|
-
|
|
1545
|
-
public static IEnumerable<FastVector3Int> AllFastPositionsWithin(this BoundsInt bounds)
|
|
1546
|
-
{
|
|
1547
|
-
Vector3Int min = bounds.min;
|
|
1548
|
-
Vector3Int max = bounds.max;
|
|
1549
|
-
for (int x = min.x; x < max.x; ++x)
|
|
1550
|
-
{
|
|
1551
|
-
for (int y = min.y; y < max.y; ++y)
|
|
1552
|
-
{
|
|
1553
|
-
for (int z = min.z; z < max.z; ++z)
|
|
1554
|
-
{
|
|
1555
|
-
yield return new FastVector3Int(x, y, z);
|
|
1556
|
-
}
|
|
1557
|
-
}
|
|
1558
|
-
}
|
|
1559
|
-
}
|
|
1560
|
-
|
|
1561
|
-
public static bool Contains(this BoundsInt bounds, FastVector3Int position)
|
|
1562
|
-
{
|
|
1563
|
-
return bounds.Contains(position);
|
|
1564
|
-
}
|
|
1565
|
-
|
|
1566
|
-
public static bool IsOnEdge2D(this FastVector3Int position, BoundsInt bounds)
|
|
1567
|
-
{
|
|
1568
|
-
if (bounds.xMin == position.x || (bounds.xMax - 1) == position.x)
|
|
1569
|
-
{
|
|
1570
|
-
return bounds.yMin <= position.y && position.y < bounds.yMax;
|
|
1571
|
-
}
|
|
1572
|
-
|
|
1573
|
-
if (bounds.yMin == position.y || (bounds.yMax - 1) == position.y)
|
|
1574
|
-
{
|
|
1575
|
-
return bounds.xMin <= position.x && position.x < bounds.xMax;
|
|
1576
|
-
}
|
|
1577
|
-
|
|
1578
|
-
return false;
|
|
1579
|
-
}
|
|
1580
|
-
|
|
1581
|
-
#if UNITY_EDITOR
|
|
1582
|
-
public static IEnumerable<Sprite> GetSpritesFromClip(this AnimationClip clip)
|
|
1583
|
-
{
|
|
1584
|
-
if (clip == null)
|
|
1585
|
-
{
|
|
1586
|
-
yield break;
|
|
1587
|
-
}
|
|
1588
|
-
|
|
1589
|
-
foreach (
|
|
1590
|
-
EditorCurveBinding binding in AnimationUtility.GetObjectReferenceCurveBindings(clip)
|
|
1591
|
-
)
|
|
1592
|
-
{
|
|
1593
|
-
ObjectReferenceKeyframe[] keyframes = AnimationUtility.GetObjectReferenceCurve(
|
|
1594
|
-
clip,
|
|
1595
|
-
binding
|
|
1596
|
-
);
|
|
1597
|
-
foreach (ObjectReferenceKeyframe frame in keyframes)
|
|
1598
|
-
{
|
|
1599
|
-
if (frame.value is Sprite sprite)
|
|
1600
|
-
{
|
|
1601
|
-
yield return sprite;
|
|
1602
|
-
}
|
|
1603
|
-
}
|
|
1604
|
-
}
|
|
1605
|
-
}
|
|
1606
|
-
#endif
|
|
1607
|
-
}
|
|
1608
|
-
}
|
|
1
|
+
namespace UnityHelpers.Core.Extension
|
|
2
|
+
{
|
|
3
|
+
using System;
|
|
4
|
+
using System.Collections.Generic;
|
|
5
|
+
using System.Linq;
|
|
6
|
+
using System.Threading.Tasks;
|
|
7
|
+
using DataStructure;
|
|
8
|
+
using DataStructure.Adapters;
|
|
9
|
+
using Helper;
|
|
10
|
+
using Random;
|
|
11
|
+
using UnityEditor;
|
|
12
|
+
using UnityEngine;
|
|
13
|
+
using UnityEngine.UI;
|
|
14
|
+
using Utils;
|
|
15
|
+
|
|
16
|
+
public static class UnityExtensions
|
|
17
|
+
{
|
|
18
|
+
public static Vector2 GetCenter(this GameObject gameObject)
|
|
19
|
+
{
|
|
20
|
+
if (gameObject.TryGetComponent(out CenterPointOffset centerPointOffset))
|
|
21
|
+
{
|
|
22
|
+
return centerPointOffset.CenterPoint;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return gameObject.transform.position;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public static Bounds Bounds(this Rect rect)
|
|
29
|
+
{
|
|
30
|
+
return new Bounds(rect.center, rect.size);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public static Rect Rect(this Bounds bounds)
|
|
34
|
+
{
|
|
35
|
+
return new Rect(bounds.center - bounds.extents, bounds.size);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public static Rect GetWorldRect(this RectTransform transform)
|
|
39
|
+
{
|
|
40
|
+
Vector3[] fourCorners = new Vector3[4];
|
|
41
|
+
transform.GetWorldCorners(fourCorners);
|
|
42
|
+
|
|
43
|
+
float[] xValues = fourCorners.Select(vector => vector.x).ToArray();
|
|
44
|
+
float[] yValues = fourCorners.Select(vector => vector.y).ToArray();
|
|
45
|
+
float minX = Mathf.Min(xValues);
|
|
46
|
+
float maxX = Mathf.Max(xValues);
|
|
47
|
+
float minY = Mathf.Min(yValues);
|
|
48
|
+
float maxY = Mathf.Max(yValues);
|
|
49
|
+
|
|
50
|
+
return new Rect(minX, minY, maxX - minX, maxY - minY);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public static Bounds OrthographicBounds(this Camera camera)
|
|
54
|
+
{
|
|
55
|
+
float screenAspect = (float)Screen.width / Screen.height;
|
|
56
|
+
float cameraHeight = camera.orthographicSize * 2;
|
|
57
|
+
Bounds bounds = new(
|
|
58
|
+
(Vector2)camera.transform.position,
|
|
59
|
+
new Vector3(cameraHeight * screenAspect, cameraHeight, 1)
|
|
60
|
+
);
|
|
61
|
+
return bounds;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public static string ToJsonString(this Vector3 vector)
|
|
65
|
+
{
|
|
66
|
+
return $"{{{vector.x}, {vector.y}, {vector.z}}}";
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public static string ToJsonString(this Vector2 vector)
|
|
70
|
+
{
|
|
71
|
+
return $"{{{vector.x}, {vector.y}}}";
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public static bool IsNoise(this Vector2 inputVector)
|
|
75
|
+
{
|
|
76
|
+
return Mathf.Abs(inputVector.x) <= 0.2f && Mathf.Abs(inputVector.y) <= 0.2f;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
public static void Stop(this Rigidbody2D rigidBody)
|
|
80
|
+
{
|
|
81
|
+
if (rigidBody == null)
|
|
82
|
+
{
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
rigidBody.velocity = Vector2.zero;
|
|
87
|
+
rigidBody.angularVelocity = 0;
|
|
88
|
+
rigidBody.Sleep();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
public static BoundsInt ExpandBounds(this BoundsInt source, BoundsInt other)
|
|
92
|
+
{
|
|
93
|
+
int xMin = Math.Min(source.xMin, other.xMin);
|
|
94
|
+
int xMax = Math.Max(source.xMax, other.xMax);
|
|
95
|
+
int yMin = Math.Min(source.yMin, other.yMin);
|
|
96
|
+
int yMax = Math.Max(source.yMax, other.yMax);
|
|
97
|
+
int zMin = Math.Min(source.zMin, other.zMin);
|
|
98
|
+
int zMax = Math.Max(source.zMax, other.zMax);
|
|
99
|
+
return new BoundsInt(xMin, yMin, zMin, xMax - xMin, yMax - yMin, zMax - zMin);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
public static BoundsInt? GetBounds(this IEnumerable<Vector3Int> positions)
|
|
103
|
+
{
|
|
104
|
+
bool any = false;
|
|
105
|
+
int xMin = int.MaxValue;
|
|
106
|
+
int xMax = int.MinValue;
|
|
107
|
+
int yMin = int.MaxValue;
|
|
108
|
+
int yMax = int.MinValue;
|
|
109
|
+
int zMin = int.MaxValue;
|
|
110
|
+
int zMax = int.MinValue;
|
|
111
|
+
foreach (Vector3Int position in positions)
|
|
112
|
+
{
|
|
113
|
+
any = true;
|
|
114
|
+
xMin = Math.Min(xMin, position.x);
|
|
115
|
+
xMax = Math.Max(xMax, position.x);
|
|
116
|
+
yMin = Math.Min(yMin, position.y);
|
|
117
|
+
yMax = Math.Max(yMax, position.y);
|
|
118
|
+
zMin = Math.Min(zMin, position.z);
|
|
119
|
+
zMax = Math.Max(zMax, position.z);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (!any)
|
|
123
|
+
{
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
return new BoundsInt(
|
|
127
|
+
xMin,
|
|
128
|
+
yMin,
|
|
129
|
+
zMin,
|
|
130
|
+
(xMax - xMin) + 1,
|
|
131
|
+
(yMax - yMin) + 1,
|
|
132
|
+
(zMax - zMin) + 1
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
public static BoundsInt? GetBounds(this IEnumerable<FastVector3Int> positions)
|
|
137
|
+
{
|
|
138
|
+
bool any = false;
|
|
139
|
+
int xMin = int.MaxValue;
|
|
140
|
+
int xMax = int.MinValue;
|
|
141
|
+
int yMin = int.MaxValue;
|
|
142
|
+
int yMax = int.MinValue;
|
|
143
|
+
int zMin = int.MaxValue;
|
|
144
|
+
int zMax = int.MinValue;
|
|
145
|
+
foreach (FastVector3Int position in positions)
|
|
146
|
+
{
|
|
147
|
+
any = true;
|
|
148
|
+
xMin = Math.Min(xMin, position.x);
|
|
149
|
+
xMax = Math.Max(xMax, position.x);
|
|
150
|
+
yMin = Math.Min(yMin, position.y);
|
|
151
|
+
yMax = Math.Max(yMax, position.y);
|
|
152
|
+
zMin = Math.Min(zMin, position.z);
|
|
153
|
+
zMax = Math.Max(zMax, position.z);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (!any)
|
|
157
|
+
{
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
return new BoundsInt(
|
|
161
|
+
xMin,
|
|
162
|
+
yMin,
|
|
163
|
+
zMin,
|
|
164
|
+
(xMax - xMin) + 1,
|
|
165
|
+
(yMax - yMin) + 1,
|
|
166
|
+
(zMax - zMin) + 1
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
public static Bounds? GetBounds(this IEnumerable<Vector2> positions)
|
|
171
|
+
{
|
|
172
|
+
bool any = false;
|
|
173
|
+
float xMin = float.MaxValue;
|
|
174
|
+
float xMax = float.MinValue;
|
|
175
|
+
float yMin = float.MaxValue;
|
|
176
|
+
float yMax = float.MinValue;
|
|
177
|
+
foreach (Vector2 position in positions)
|
|
178
|
+
{
|
|
179
|
+
any = true;
|
|
180
|
+
xMin = Math.Min(xMin, position.x);
|
|
181
|
+
xMax = Math.Max(xMax, position.x);
|
|
182
|
+
yMin = Math.Min(yMin, position.y);
|
|
183
|
+
yMax = Math.Max(yMax, position.y);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (!any)
|
|
187
|
+
{
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
Vector3 center = new((xMax + xMin) / 2f, (yMax + yMin) / 2f);
|
|
192
|
+
Vector3 size = new(xMax - xMin, yMax - yMin);
|
|
193
|
+
return new Bounds(center, size);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
public static Bounds? GetBounds(this IEnumerable<Bounds> boundaries)
|
|
197
|
+
{
|
|
198
|
+
float minX = float.MaxValue;
|
|
199
|
+
float minY = float.MaxValue;
|
|
200
|
+
float maxX = float.MinValue;
|
|
201
|
+
float maxY = float.MinValue;
|
|
202
|
+
bool any = false;
|
|
203
|
+
foreach (Bounds boundary in boundaries)
|
|
204
|
+
{
|
|
205
|
+
any = true;
|
|
206
|
+
Vector3 min = boundary.min;
|
|
207
|
+
Vector3 max = boundary.max;
|
|
208
|
+
minX = Math.Min(minX, min.x);
|
|
209
|
+
maxX = Math.Max(maxX, max.x);
|
|
210
|
+
minY = Math.Min(minY, min.y);
|
|
211
|
+
maxY = Math.Max(maxY, max.y);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (!any)
|
|
215
|
+
{
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return new Bounds(
|
|
220
|
+
new Vector3(minX + (maxX - minX) / 2, minY + (maxY - minY) / 2),
|
|
221
|
+
new Vector3(maxX - minX, maxY - minY)
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// https://www.habrador.com/tutorials/math/8-convex-hull/
|
|
226
|
+
public static List<Vector3Int> BuildConvexHull(
|
|
227
|
+
this IEnumerable<Vector3Int> pointsSet,
|
|
228
|
+
Grid grid,
|
|
229
|
+
IRandom random = null,
|
|
230
|
+
bool includeColinearPoints = true
|
|
231
|
+
)
|
|
232
|
+
{
|
|
233
|
+
List<Vector3Int> points = pointsSet.ToList();
|
|
234
|
+
if (points.Count <= 3)
|
|
235
|
+
{
|
|
236
|
+
return points;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
random ??= PRNG.Instance;
|
|
240
|
+
|
|
241
|
+
Vector2 CellToWorld(Vector3Int position) => grid.CellToWorld(position);
|
|
242
|
+
|
|
243
|
+
Vector3Int startPoint = points[0];
|
|
244
|
+
Vector2 startPointWorldPosition = CellToWorld(startPoint);
|
|
245
|
+
for (int i = 1; i < points.Count; ++i)
|
|
246
|
+
{
|
|
247
|
+
Vector3Int testPoint = points[i];
|
|
248
|
+
Vector2 testPointWorldPosition = CellToWorld(testPoint);
|
|
249
|
+
// ReSharper disable once CompareOfFloatsByEqualityOperator
|
|
250
|
+
if (
|
|
251
|
+
testPointWorldPosition.x < startPointWorldPosition.x
|
|
252
|
+
|| (
|
|
253
|
+
Mathf.Approximately(testPointWorldPosition.x, startPointWorldPosition.x)
|
|
254
|
+
&& testPointWorldPosition.y < startPointWorldPosition.y
|
|
255
|
+
)
|
|
256
|
+
)
|
|
257
|
+
{
|
|
258
|
+
startPoint = testPoint;
|
|
259
|
+
startPointWorldPosition = testPointWorldPosition;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
List<Vector3Int> convexHull = new() { startPoint };
|
|
264
|
+
_ = points.Remove(startPoint);
|
|
265
|
+
Vector3Int currentPoint = convexHull[0];
|
|
266
|
+
List<Vector3Int> colinearPoints = new();
|
|
267
|
+
int counter = 0;
|
|
268
|
+
while (true)
|
|
269
|
+
{
|
|
270
|
+
if (counter == 2)
|
|
271
|
+
{
|
|
272
|
+
points.Add(convexHull[0]);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (points.Count <= 0)
|
|
276
|
+
{
|
|
277
|
+
return convexHull;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
Vector3Int nextPoint = random.NextOf(points);
|
|
281
|
+
Vector2 currentPointWorldPosition = CellToWorld(currentPoint);
|
|
282
|
+
Vector2 nextPointWorldPosition = CellToWorld(nextPoint);
|
|
283
|
+
foreach (Vector3Int point in points)
|
|
284
|
+
{
|
|
285
|
+
if (Equals(point, nextPoint))
|
|
286
|
+
{
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
float relation = Geometry.IsAPointLeftOfVectorOrOnTheLine(
|
|
291
|
+
currentPointWorldPosition,
|
|
292
|
+
nextPointWorldPosition,
|
|
293
|
+
CellToWorld(point)
|
|
294
|
+
);
|
|
295
|
+
if (Mathf.Approximately(relation, 0))
|
|
296
|
+
{
|
|
297
|
+
colinearPoints.Add(point);
|
|
298
|
+
}
|
|
299
|
+
else if (relation < 0)
|
|
300
|
+
{
|
|
301
|
+
nextPoint = point;
|
|
302
|
+
nextPointWorldPosition = CellToWorld(nextPoint);
|
|
303
|
+
colinearPoints.Clear();
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (0 < colinearPoints.Count)
|
|
308
|
+
{
|
|
309
|
+
colinearPoints.Add(nextPoint);
|
|
310
|
+
colinearPoints.Sort(
|
|
311
|
+
(lhs, rhs) =>
|
|
312
|
+
(CellToWorld(lhs) - currentPointWorldPosition).sqrMagnitude.CompareTo(
|
|
313
|
+
(CellToWorld(rhs) - currentPointWorldPosition).sqrMagnitude
|
|
314
|
+
)
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
if (includeColinearPoints)
|
|
318
|
+
{
|
|
319
|
+
convexHull.AddRange(colinearPoints);
|
|
320
|
+
}
|
|
321
|
+
else
|
|
322
|
+
{
|
|
323
|
+
convexHull.Add(colinearPoints[^1]);
|
|
324
|
+
_ = points.Remove(colinearPoints[^1]);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
currentPoint = colinearPoints[^1];
|
|
328
|
+
_ = points.RemoveAll(colinearPoints.Contains);
|
|
329
|
+
colinearPoints.Clear();
|
|
330
|
+
}
|
|
331
|
+
else
|
|
332
|
+
{
|
|
333
|
+
convexHull.Add(nextPoint);
|
|
334
|
+
_ = points.Remove(nextPoint);
|
|
335
|
+
currentPoint = nextPoint;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (Equals(currentPoint, convexHull[0]))
|
|
339
|
+
{
|
|
340
|
+
convexHull.RemoveAt(convexHull.Count - 1);
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
++counter;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return convexHull;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
public static List<FastVector3Int> BuildConvexHull(
|
|
351
|
+
this IEnumerable<FastVector3Int> pointsSet,
|
|
352
|
+
Grid grid,
|
|
353
|
+
IRandom random = null,
|
|
354
|
+
bool includeColinearPoints = false
|
|
355
|
+
)
|
|
356
|
+
{
|
|
357
|
+
List<FastVector3Int> points = pointsSet.ToList();
|
|
358
|
+
if (points.Count <= 3)
|
|
359
|
+
{
|
|
360
|
+
return points;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
random ??= PRNG.Instance;
|
|
364
|
+
|
|
365
|
+
Vector2 CellToWorld(FastVector3Int position) => grid.CellToWorld(position);
|
|
366
|
+
|
|
367
|
+
FastVector3Int startPoint = points[0];
|
|
368
|
+
Vector2 startPointWorldPosition = CellToWorld(startPoint);
|
|
369
|
+
for (int i = 1; i < points.Count; ++i)
|
|
370
|
+
{
|
|
371
|
+
FastVector3Int testPoint = points[i];
|
|
372
|
+
Vector2 testPointWorldPosition = CellToWorld(testPoint);
|
|
373
|
+
if (
|
|
374
|
+
testPointWorldPosition.x < startPointWorldPosition.x
|
|
375
|
+
|| (
|
|
376
|
+
Mathf.Approximately(testPointWorldPosition.x, startPointWorldPosition.x)
|
|
377
|
+
&& testPointWorldPosition.y < startPointWorldPosition.y
|
|
378
|
+
)
|
|
379
|
+
)
|
|
380
|
+
{
|
|
381
|
+
startPoint = testPoint;
|
|
382
|
+
startPointWorldPosition = testPointWorldPosition;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
List<FastVector3Int> convexHull = new() { startPoint };
|
|
387
|
+
_ = points.Remove(startPoint);
|
|
388
|
+
FastVector3Int currentPoint = convexHull[0];
|
|
389
|
+
List<FastVector3Int> colinearPoints = new();
|
|
390
|
+
int counter = 0;
|
|
391
|
+
while (true)
|
|
392
|
+
{
|
|
393
|
+
if (counter == 2)
|
|
394
|
+
{
|
|
395
|
+
points.Add(convexHull[0]);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (points.Count <= 0)
|
|
399
|
+
{
|
|
400
|
+
return convexHull;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
FastVector3Int nextPoint = random.NextOf(points);
|
|
404
|
+
Vector2 currentPointWorldPosition = CellToWorld(currentPoint);
|
|
405
|
+
Vector2 nextPointWorldPosition = CellToWorld(nextPoint);
|
|
406
|
+
foreach (FastVector3Int point in points)
|
|
407
|
+
{
|
|
408
|
+
if (point.Equals(nextPoint))
|
|
409
|
+
{
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
float relation = Geometry.IsAPointLeftOfVectorOrOnTheLine(
|
|
414
|
+
currentPointWorldPosition,
|
|
415
|
+
nextPointWorldPosition,
|
|
416
|
+
CellToWorld(point)
|
|
417
|
+
);
|
|
418
|
+
if (Mathf.Approximately(relation, 0))
|
|
419
|
+
{
|
|
420
|
+
colinearPoints.Add(point);
|
|
421
|
+
}
|
|
422
|
+
else if (relation < 0)
|
|
423
|
+
{
|
|
424
|
+
nextPoint = point;
|
|
425
|
+
nextPointWorldPosition = CellToWorld(nextPoint);
|
|
426
|
+
colinearPoints.Clear();
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (0 < colinearPoints.Count)
|
|
431
|
+
{
|
|
432
|
+
colinearPoints.Add(nextPoint);
|
|
433
|
+
colinearPoints.Sort(
|
|
434
|
+
(lhs, rhs) =>
|
|
435
|
+
(CellToWorld(lhs) - currentPointWorldPosition).sqrMagnitude.CompareTo(
|
|
436
|
+
(CellToWorld(rhs) - currentPointWorldPosition).sqrMagnitude
|
|
437
|
+
)
|
|
438
|
+
);
|
|
439
|
+
|
|
440
|
+
if (includeColinearPoints)
|
|
441
|
+
{
|
|
442
|
+
convexHull.AddRange(colinearPoints);
|
|
443
|
+
}
|
|
444
|
+
else
|
|
445
|
+
{
|
|
446
|
+
convexHull.Add(colinearPoints[^1]);
|
|
447
|
+
_ = points.Remove(colinearPoints[^1]);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
currentPoint = colinearPoints[^1];
|
|
451
|
+
_ = points.RemoveAll(colinearPoints.Contains);
|
|
452
|
+
colinearPoints.Clear();
|
|
453
|
+
}
|
|
454
|
+
else
|
|
455
|
+
{
|
|
456
|
+
convexHull.Add(nextPoint);
|
|
457
|
+
_ = points.Remove(nextPoint);
|
|
458
|
+
currentPoint = nextPoint;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (currentPoint.Equals(convexHull[0]))
|
|
462
|
+
{
|
|
463
|
+
convexHull.RemoveAt(convexHull.Count - 1);
|
|
464
|
+
break;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
++counter;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
return convexHull;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
public static bool IsConvexHullInsideConvexHull(
|
|
474
|
+
this List<FastVector3Int> convexHull,
|
|
475
|
+
Grid grid,
|
|
476
|
+
List<FastVector3Int> maybeInside
|
|
477
|
+
)
|
|
478
|
+
{
|
|
479
|
+
foreach (FastVector3Int point in maybeInside)
|
|
480
|
+
{
|
|
481
|
+
if (!IsPointInsideConvexHull(convexHull, grid, point))
|
|
482
|
+
{
|
|
483
|
+
return false;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
return true;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
public static bool IsPointInsideConvexHull(
|
|
491
|
+
this List<Vector3Int> convexHull,
|
|
492
|
+
Grid grid,
|
|
493
|
+
Vector3Int point
|
|
494
|
+
)
|
|
495
|
+
{
|
|
496
|
+
for (int i = 0; i < convexHull.Count; ++i)
|
|
497
|
+
{
|
|
498
|
+
Vector3Int lhs = convexHull[i];
|
|
499
|
+
Vector3Int rhs = convexHull[(i + 1) % convexHull.Count];
|
|
500
|
+
float relation = Geometry.IsAPointLeftOfVectorOrOnTheLine(
|
|
501
|
+
grid.CellToWorld(lhs),
|
|
502
|
+
grid.CellToWorld(rhs),
|
|
503
|
+
grid.CellToWorld(point)
|
|
504
|
+
);
|
|
505
|
+
if (relation < 0)
|
|
506
|
+
{
|
|
507
|
+
return false;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
return true;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
public static bool IsPointInsideConvexHull(
|
|
514
|
+
this List<FastVector3Int> convexHull,
|
|
515
|
+
Grid grid,
|
|
516
|
+
FastVector3Int point
|
|
517
|
+
)
|
|
518
|
+
{
|
|
519
|
+
for (int i = 0; i < convexHull.Count; ++i)
|
|
520
|
+
{
|
|
521
|
+
FastVector3Int lhs = convexHull[i];
|
|
522
|
+
FastVector3Int rhs = convexHull[(i + 1) % convexHull.Count];
|
|
523
|
+
float relation = Geometry.IsAPointLeftOfVectorOrOnTheLine(
|
|
524
|
+
grid.CellToWorld(lhs),
|
|
525
|
+
grid.CellToWorld(rhs),
|
|
526
|
+
grid.CellToWorld(point)
|
|
527
|
+
);
|
|
528
|
+
if (relation < 0)
|
|
529
|
+
{
|
|
530
|
+
return false;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
return true;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
public static bool IsConvexHullInsideConvexHull(
|
|
537
|
+
this List<Vector3Int> convexHull,
|
|
538
|
+
Grid grid,
|
|
539
|
+
List<Vector3Int> maybeInside
|
|
540
|
+
)
|
|
541
|
+
{
|
|
542
|
+
foreach (Vector3Int point in maybeInside)
|
|
543
|
+
{
|
|
544
|
+
if (!IsPointInsideConvexHull(convexHull, grid, point))
|
|
545
|
+
{
|
|
546
|
+
return false;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
return true;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
private readonly struct HullEdge
|
|
554
|
+
{
|
|
555
|
+
public readonly float edgeLength;
|
|
556
|
+
|
|
557
|
+
public readonly FastVector3Int from;
|
|
558
|
+
public readonly FastVector3Int to;
|
|
559
|
+
|
|
560
|
+
public readonly Vector2 fromWorld;
|
|
561
|
+
public readonly Vector2 toWorld;
|
|
562
|
+
|
|
563
|
+
private readonly Grid _grid;
|
|
564
|
+
|
|
565
|
+
public HullEdge(FastVector3Int from, FastVector3Int to, Grid grid)
|
|
566
|
+
{
|
|
567
|
+
this.from = from;
|
|
568
|
+
this.to = to;
|
|
569
|
+
_grid = grid;
|
|
570
|
+
fromWorld = grid.CellToWorld(from);
|
|
571
|
+
toWorld = grid.CellToWorld(to);
|
|
572
|
+
edgeLength = (fromWorld - toWorld).sqrMagnitude;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
public bool Intersects(HullEdge other)
|
|
576
|
+
{
|
|
577
|
+
return UnityExtensions.Intersects(
|
|
578
|
+
fromWorld,
|
|
579
|
+
toWorld,
|
|
580
|
+
other.fromWorld,
|
|
581
|
+
other.toWorld
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
public float LargestAngle(FastVector3Int point)
|
|
586
|
+
{
|
|
587
|
+
Vector2 worldPoint = _grid.CellToWorld(point);
|
|
588
|
+
float angleFrom = Vector2.Angle((toWorld - fromWorld), (worldPoint - fromWorld));
|
|
589
|
+
float angleTo = Vector2.Angle((fromWorld - toWorld), (worldPoint - toWorld));
|
|
590
|
+
return Math.Max(angleFrom, angleTo);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
private sealed class ConcaveHullComparer : IComparer<HullEdge>
|
|
595
|
+
{
|
|
596
|
+
public static readonly ConcaveHullComparer Instance = new();
|
|
597
|
+
|
|
598
|
+
private ConcaveHullComparer() { }
|
|
599
|
+
|
|
600
|
+
public int Compare(HullEdge lhs, HullEdge rhs)
|
|
601
|
+
{
|
|
602
|
+
int comparison = lhs.edgeLength.CompareTo(rhs.edgeLength);
|
|
603
|
+
if (comparison != 0)
|
|
604
|
+
{
|
|
605
|
+
return comparison;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
comparison = lhs.from.CompareTo(rhs.from);
|
|
609
|
+
if (comparison != 0)
|
|
610
|
+
{
|
|
611
|
+
return comparison;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
return lhs.to.CompareTo(rhs.to);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
public static List<FastVector3Int> BuildConcaveHull3(
|
|
619
|
+
this IReadOnlyCollection<FastVector3Int> gridPositions,
|
|
620
|
+
Grid grid,
|
|
621
|
+
IRandom random = null,
|
|
622
|
+
int bucketSize = 40,
|
|
623
|
+
float angleThreshold = 90f
|
|
624
|
+
)
|
|
625
|
+
{
|
|
626
|
+
List<FastVector3Int> convexHull = gridPositions.BuildConvexHull(grid, random);
|
|
627
|
+
List<HullEdge> concaveHullEdges = new();
|
|
628
|
+
|
|
629
|
+
SortedSet<HullEdge> data = new(ConcaveHullComparer.Instance);
|
|
630
|
+
for (int i = 0; i < convexHull.Count; ++i)
|
|
631
|
+
{
|
|
632
|
+
FastVector3Int lhs = convexHull[i];
|
|
633
|
+
FastVector3Int rhs = convexHull[(i + 1) % convexHull.Count];
|
|
634
|
+
HullEdge edge = new(lhs, rhs, grid);
|
|
635
|
+
_ = data.Add(edge);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
HashSet<FastVector3Int> remainingPoints = gridPositions.ToHashSet();
|
|
639
|
+
remainingPoints.ExceptWith(convexHull);
|
|
640
|
+
|
|
641
|
+
Vector2 CellToWorld(FastVector3Int cell) => grid.CellToWorld(cell);
|
|
642
|
+
|
|
643
|
+
Bounds? maybeBounds = gridPositions.Select(CellToWorld).GetBounds();
|
|
644
|
+
if (maybeBounds == null)
|
|
645
|
+
{
|
|
646
|
+
throw new ArgumentException(nameof(gridPositions));
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
QuadTree<FastVector3Int> NewQuadTree() =>
|
|
650
|
+
new(gridPositions, CellToWorld, maybeBounds.Value, bucketSize: bucketSize);
|
|
651
|
+
|
|
652
|
+
QuadTree<FastVector3Int> quadTree = NewQuadTree();
|
|
653
|
+
List<FastVector3Int> neighbors = Buffers<FastVector3Int>.List;
|
|
654
|
+
while (0 < data.Count)
|
|
655
|
+
{
|
|
656
|
+
HullEdge edge = data.Max;
|
|
657
|
+
_ = data.Remove(edge);
|
|
658
|
+
|
|
659
|
+
Vector2 edgeCenter = edge.fromWorld + (edge.toWorld - edge.fromWorld) / 2;
|
|
660
|
+
quadTree.GetApproximateNearestNeighbors(edgeCenter, bucketSize, neighbors);
|
|
661
|
+
float localMaximumDistance = float.MinValue;
|
|
662
|
+
foreach (FastVector3Int neighbor in neighbors)
|
|
663
|
+
{
|
|
664
|
+
if (neighbor == edge.to || neighbor == edge.from)
|
|
665
|
+
{
|
|
666
|
+
continue;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
localMaximumDistance = Math.Max(
|
|
670
|
+
localMaximumDistance,
|
|
671
|
+
(CellToWorld(neighbor) - edgeCenter).sqrMagnitude
|
|
672
|
+
);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
if (edge.edgeLength <= localMaximumDistance)
|
|
676
|
+
{
|
|
677
|
+
concaveHullEdges.Add(edge);
|
|
678
|
+
continue;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
float smallestAngle = float.MaxValue;
|
|
682
|
+
FastVector3Int? maybeChosenPoint = null;
|
|
683
|
+
foreach (FastVector3Int remainingPoint in remainingPoints)
|
|
684
|
+
{
|
|
685
|
+
float maximumAngle = edge.LargestAngle(remainingPoint);
|
|
686
|
+
if (maximumAngle < smallestAngle)
|
|
687
|
+
{
|
|
688
|
+
maybeChosenPoint = remainingPoint;
|
|
689
|
+
smallestAngle = maximumAngle;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
if (angleThreshold < smallestAngle)
|
|
694
|
+
{
|
|
695
|
+
concaveHullEdges.Add(edge);
|
|
696
|
+
continue;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
if (maybeChosenPoint == null)
|
|
700
|
+
{
|
|
701
|
+
concaveHullEdges.Add(edge);
|
|
702
|
+
continue;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
FastVector3Int chosenPoint = maybeChosenPoint.Value;
|
|
706
|
+
HullEdge e2 = new(edge.from, chosenPoint, grid);
|
|
707
|
+
HullEdge e3 = new(chosenPoint, edge.to, grid);
|
|
708
|
+
bool intersects = false;
|
|
709
|
+
foreach (HullEdge convexHullEdge in data)
|
|
710
|
+
{
|
|
711
|
+
if (convexHullEdge.Intersects(e2))
|
|
712
|
+
{
|
|
713
|
+
intersects = true;
|
|
714
|
+
break;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
if (convexHullEdge.Intersects(e3))
|
|
718
|
+
{
|
|
719
|
+
intersects = true;
|
|
720
|
+
break;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
if (!intersects)
|
|
725
|
+
{
|
|
726
|
+
foreach (HullEdge concaveHullEdge in concaveHullEdges)
|
|
727
|
+
{
|
|
728
|
+
if (concaveHullEdge.Intersects(e2))
|
|
729
|
+
{
|
|
730
|
+
intersects = true;
|
|
731
|
+
break;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
if (concaveHullEdge.Intersects(e3))
|
|
735
|
+
{
|
|
736
|
+
intersects = true;
|
|
737
|
+
break;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
if (!intersects)
|
|
743
|
+
{
|
|
744
|
+
_ = data.Add(e2);
|
|
745
|
+
_ = data.Add(e3);
|
|
746
|
+
_ = remainingPoints.Remove(maybeChosenPoint.Value);
|
|
747
|
+
}
|
|
748
|
+
else
|
|
749
|
+
{
|
|
750
|
+
concaveHullEdges.Add(edge);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
List<FastVector3Int> concaveHull = new(concaveHullEdges.Count);
|
|
755
|
+
HullEdge current = concaveHullEdges[0];
|
|
756
|
+
concaveHullEdges.RemoveAtSwapBack(0);
|
|
757
|
+
concaveHull.Add(current.from);
|
|
758
|
+
while (0 < concaveHullEdges.Count)
|
|
759
|
+
{
|
|
760
|
+
FastVector3Int to = current.to;
|
|
761
|
+
int nextIndex = concaveHullEdges.FindIndex(edge => edge.from == to);
|
|
762
|
+
current = concaveHullEdges[nextIndex];
|
|
763
|
+
concaveHullEdges.RemoveAtSwapBack(nextIndex);
|
|
764
|
+
concaveHull.Add(current.from);
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
return concaveHull;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
// https://www.researchgate.net/publication/220868874_Concave_hull_A_k-nearest_neighbours_approach_for_the_computation_of_the_region_occupied_by_a_set_of_points
|
|
771
|
+
|
|
772
|
+
public static List<FastVector3Int> BuildConcaveHull2(
|
|
773
|
+
this IReadOnlyCollection<FastVector3Int> gridPositions,
|
|
774
|
+
Grid grid,
|
|
775
|
+
IRandom random = null,
|
|
776
|
+
int nearestNeighbors = 3
|
|
777
|
+
)
|
|
778
|
+
{
|
|
779
|
+
const int minimumNearestNeighbors = 3;
|
|
780
|
+
nearestNeighbors = Math.Max(minimumNearestNeighbors, nearestNeighbors);
|
|
781
|
+
List<FastVector3Int> dataSet = gridPositions.Distinct().ToList();
|
|
782
|
+
if (dataSet.Count <= 3)
|
|
783
|
+
{
|
|
784
|
+
return dataSet;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
nearestNeighbors = Math.Min(dataSet.Count, nearestNeighbors);
|
|
788
|
+
|
|
789
|
+
IComparer<FastVector3Int> comparison = Comparer<FastVector3Int>.Create(
|
|
790
|
+
(lhs, rhs) => grid.CellToWorld(lhs).y.CompareTo(grid.CellToWorld(rhs).y)
|
|
791
|
+
);
|
|
792
|
+
|
|
793
|
+
FastVector3Int? maybeFirst = null;
|
|
794
|
+
foreach (FastVector3Int gridPosition in dataSet)
|
|
795
|
+
{
|
|
796
|
+
if (maybeFirst == null || comparison.Compare(gridPosition, maybeFirst.Value) < 0)
|
|
797
|
+
{
|
|
798
|
+
maybeFirst = gridPosition;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
if (maybeFirst == null)
|
|
803
|
+
{
|
|
804
|
+
return dataSet;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
FastVector3Int first = maybeFirst.Value;
|
|
808
|
+
List<FastVector3Int> hull = new(dataSet.Count) { first };
|
|
809
|
+
int step = 2;
|
|
810
|
+
float previousAngle = 0f;
|
|
811
|
+
FastVector3Int current = first;
|
|
812
|
+
_ = dataSet.Remove(current);
|
|
813
|
+
|
|
814
|
+
float CalculateAngle(Vector2 lhs, Vector2 rhs)
|
|
815
|
+
{
|
|
816
|
+
return Mathf.Atan2(rhs.y - lhs.y, rhs.x - lhs.x);
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// https://github.com/merowech/java-concave-hull/blob/master/ConcaveHull.java
|
|
820
|
+
float AngleDifference(float lhsAngle, float rhsAngle)
|
|
821
|
+
{
|
|
822
|
+
if (0 < lhsAngle && 0 <= rhsAngle && rhsAngle < lhsAngle)
|
|
823
|
+
{
|
|
824
|
+
return Math.Abs(lhsAngle - rhsAngle);
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
if (0 <= lhsAngle && 0 < rhsAngle && lhsAngle < rhsAngle)
|
|
828
|
+
{
|
|
829
|
+
return 2 * Mathf.PI + lhsAngle - rhsAngle;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
if (lhsAngle < 0 && rhsAngle <= 0 && lhsAngle < rhsAngle)
|
|
833
|
+
{
|
|
834
|
+
return 2 * Mathf.PI + lhsAngle + Math.Abs(rhsAngle);
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
if (lhsAngle <= 0 && rhsAngle < 0 && rhsAngle < lhsAngle)
|
|
838
|
+
{
|
|
839
|
+
return Math.Abs(lhsAngle - rhsAngle);
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
if (lhsAngle <= 0 && 0 < rhsAngle)
|
|
843
|
+
{
|
|
844
|
+
return 2 * Mathf.PI + lhsAngle - rhsAngle;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
if (0 <= lhsAngle && rhsAngle <= 0)
|
|
848
|
+
{
|
|
849
|
+
return lhsAngle + Math.Abs(rhsAngle);
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
return 0f;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
// Order by descending right hand turns
|
|
856
|
+
int RightHandTurnComparison(FastVector3Int lhs, FastVector3Int rhs)
|
|
857
|
+
{
|
|
858
|
+
// TODO: I think this is fucked
|
|
859
|
+
Vector2 currentPoint = grid.CellToWorld(current);
|
|
860
|
+
Vector2 lhsPoint = grid.CellToWorld(lhs);
|
|
861
|
+
Vector2 rhsPoint = grid.CellToWorld(rhs);
|
|
862
|
+
|
|
863
|
+
float lhsAngle = AngleDifference(
|
|
864
|
+
previousAngle,
|
|
865
|
+
CalculateAngle(currentPoint, lhsPoint)
|
|
866
|
+
);
|
|
867
|
+
float rhsAngle = AngleDifference(
|
|
868
|
+
previousAngle,
|
|
869
|
+
CalculateAngle(currentPoint, rhsPoint)
|
|
870
|
+
);
|
|
871
|
+
return rhsAngle.CompareTo(lhsAngle);
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
List<FastVector3Int> clockwisePoints = Buffers<FastVector3Int>.List;
|
|
875
|
+
void FindNearestNeighborsAndPutInClockwisePoints()
|
|
876
|
+
{
|
|
877
|
+
clockwisePoints.Clear();
|
|
878
|
+
clockwisePoints.AddRange(dataSet);
|
|
879
|
+
Vector2 currentPoint = grid.CellToWorld(current);
|
|
880
|
+
clockwisePoints.Sort(
|
|
881
|
+
(lhs, rhs) =>
|
|
882
|
+
{
|
|
883
|
+
Vector2 lhsPoint = grid.CellToWorld(lhs);
|
|
884
|
+
Vector2 rhsPoint = grid.CellToWorld(rhs);
|
|
885
|
+
return (lhsPoint - currentPoint).sqrMagnitude.CompareTo(
|
|
886
|
+
(rhsPoint - currentPoint).sqrMagnitude
|
|
887
|
+
);
|
|
888
|
+
}
|
|
889
|
+
);
|
|
890
|
+
if (nearestNeighbors < clockwisePoints.Count)
|
|
891
|
+
{
|
|
892
|
+
clockwisePoints.RemoveRange(
|
|
893
|
+
nearestNeighbors,
|
|
894
|
+
clockwisePoints.Count - nearestNeighbors
|
|
895
|
+
);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
while (0 < dataSet.Count)
|
|
900
|
+
{
|
|
901
|
+
if (step == 5)
|
|
902
|
+
{
|
|
903
|
+
dataSet.Add(first);
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
FindNearestNeighborsAndPutInClockwisePoints();
|
|
907
|
+
clockwisePoints.Sort(RightHandTurnComparison);
|
|
908
|
+
|
|
909
|
+
bool intersects = true;
|
|
910
|
+
int i = -1;
|
|
911
|
+
while (intersects && i < clockwisePoints.Count - 1)
|
|
912
|
+
{
|
|
913
|
+
++i;
|
|
914
|
+
|
|
915
|
+
FastVector3Int indexedPoint = clockwisePoints[i];
|
|
916
|
+
int lastPoint = indexedPoint == first ? 1 : 0;
|
|
917
|
+
int j = 2;
|
|
918
|
+
intersects = false;
|
|
919
|
+
Vector2 lhsTo = grid.CellToWorld(indexedPoint);
|
|
920
|
+
while (!intersects && j < hull.Count - lastPoint)
|
|
921
|
+
{
|
|
922
|
+
Vector2 lhsFrom = grid.CellToWorld(hull[step - 2]);
|
|
923
|
+
Vector2 rhsFrom = grid.CellToWorld(hull[step - 2 - j]);
|
|
924
|
+
Vector2 rhsTo = grid.CellToWorld(hull[step - 1 - j]);
|
|
925
|
+
intersects = Intersects(lhsFrom, lhsTo, rhsFrom, rhsTo);
|
|
926
|
+
++j;
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
if (intersects)
|
|
931
|
+
{
|
|
932
|
+
for (i = dataSet.Count - 1; 0 <= i; --i)
|
|
933
|
+
{
|
|
934
|
+
if (!IsPositionInside(hull, dataSet[i], grid))
|
|
935
|
+
{
|
|
936
|
+
return BuildConcaveHull2(
|
|
937
|
+
gridPositions,
|
|
938
|
+
grid,
|
|
939
|
+
random,
|
|
940
|
+
nearestNeighbors + 1
|
|
941
|
+
);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
return hull;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
current = clockwisePoints[i];
|
|
949
|
+
if (current != first)
|
|
950
|
+
{
|
|
951
|
+
hull.Add(current);
|
|
952
|
+
}
|
|
953
|
+
else
|
|
954
|
+
{
|
|
955
|
+
break;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
int currentIndex = dataSet.IndexOf(current);
|
|
959
|
+
if (0 <= currentIndex)
|
|
960
|
+
{
|
|
961
|
+
dataSet.RemoveAtSwapBack(currentIndex);
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
previousAngle = CalculateAngle(
|
|
965
|
+
grid.CellToWorld(hull[step - 1]),
|
|
966
|
+
grid.CellToWorld(hull[step - 2])
|
|
967
|
+
);
|
|
968
|
+
++step;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
for (int i = dataSet.Count - 1; 0 <= i; --i)
|
|
972
|
+
{
|
|
973
|
+
if (!IsPositionInside(hull, dataSet[i], grid))
|
|
974
|
+
{
|
|
975
|
+
return BuildConcaveHull2(gridPositions, grid, random, nearestNeighbors + 1);
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
return hull;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
// This one has bugs, user beware
|
|
983
|
+
// https://github.com/Liagson/ConcaveHullGenerator/tree/master
|
|
984
|
+
#region ConcaveHull Functions
|
|
985
|
+
|
|
986
|
+
private readonly struct Line
|
|
987
|
+
{
|
|
988
|
+
public readonly double sqrMagnitude;
|
|
989
|
+
|
|
990
|
+
public readonly Vector2 from;
|
|
991
|
+
public readonly Vector2 to;
|
|
992
|
+
|
|
993
|
+
public Line(Vector2 from, Vector2 to)
|
|
994
|
+
{
|
|
995
|
+
this.from = from;
|
|
996
|
+
this.to = to;
|
|
997
|
+
sqrMagnitude = (from - to).sqrMagnitude;
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
public static List<FastVector3Int> BuildConcaveHull(
|
|
1002
|
+
this IEnumerable<FastVector3Int> gridPositions,
|
|
1003
|
+
Grid grid,
|
|
1004
|
+
IRandom random = null,
|
|
1005
|
+
float scaleFactor = 1,
|
|
1006
|
+
float concavity = 0f
|
|
1007
|
+
)
|
|
1008
|
+
{
|
|
1009
|
+
if (concavity < -1 || 1 < concavity)
|
|
1010
|
+
{
|
|
1011
|
+
throw new ArgumentException($"Concavity must be between [-1, 1], was {concavity}");
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
List<FastVector3Int> originalGridPositions = gridPositions.ToList();
|
|
1015
|
+
if (originalGridPositions.Count <= 3)
|
|
1016
|
+
{
|
|
1017
|
+
return originalGridPositions;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
List<FastVector3Int> convexHull = originalGridPositions.BuildConvexHull(grid, random);
|
|
1021
|
+
HashSet<FastVector3Int> unusedNodes = originalGridPositions.ToHashSet();
|
|
1022
|
+
unusedNodes.ExceptWith(convexHull);
|
|
1023
|
+
List<Line> concaveHullLines = new(convexHull.Count);
|
|
1024
|
+
for (int i = 0; i < convexHull.Count; ++i)
|
|
1025
|
+
{
|
|
1026
|
+
FastVector3Int lhsGridPoint = convexHull[i];
|
|
1027
|
+
FastVector3Int rhsGridPoint = convexHull[(i + 1) % convexHull.Count];
|
|
1028
|
+
Vector2 lhs = grid.CellToWorld(lhsGridPoint);
|
|
1029
|
+
Vector2 rhs = grid.CellToWorld(rhsGridPoint);
|
|
1030
|
+
Line line = new(lhs, rhs);
|
|
1031
|
+
concaveHullLines.Add(line);
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
bool aLineWasDividedInTheIteration;
|
|
1035
|
+
do
|
|
1036
|
+
{
|
|
1037
|
+
// Order by descending
|
|
1038
|
+
concaveHullLines.Sort((lhs, rhs) => rhs.sqrMagnitude.CompareTo(lhs.sqrMagnitude));
|
|
1039
|
+
|
|
1040
|
+
aLineWasDividedInTheIteration = false;
|
|
1041
|
+
for (int i = 0; i < concaveHullLines.Count; ++i)
|
|
1042
|
+
{
|
|
1043
|
+
Line line = concaveHullLines[i];
|
|
1044
|
+
IEnumerable<FastVector3Int> nearbyPoints = GetNearbyPoints(
|
|
1045
|
+
line,
|
|
1046
|
+
unusedNodes,
|
|
1047
|
+
grid,
|
|
1048
|
+
scaleFactor
|
|
1049
|
+
);
|
|
1050
|
+
List<Line> dividedLine = GetDividedLine(
|
|
1051
|
+
line,
|
|
1052
|
+
nearbyPoints,
|
|
1053
|
+
concaveHullLines,
|
|
1054
|
+
grid,
|
|
1055
|
+
concavity
|
|
1056
|
+
);
|
|
1057
|
+
if (0 < dividedLine.Count)
|
|
1058
|
+
{
|
|
1059
|
+
aLineWasDividedInTheIteration = true;
|
|
1060
|
+
FastVector3Int toRemove = grid.WorldToCell(dividedLine[0].to);
|
|
1061
|
+
_ = unusedNodes.Remove(toRemove);
|
|
1062
|
+
concaveHullLines.AddRange(dividedLine);
|
|
1063
|
+
concaveHullLines.RemoveAtSwapBack(i);
|
|
1064
|
+
break;
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
} while (aLineWasDividedInTheIteration);
|
|
1068
|
+
|
|
1069
|
+
List<FastVector3Int> concaveHull = new(concaveHullLines.Count);
|
|
1070
|
+
if (concaveHullLines.Count <= 0)
|
|
1071
|
+
{
|
|
1072
|
+
return concaveHull;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
Line currentlyConsideredLine = concaveHullLines[0];
|
|
1076
|
+
FastVector3Int from = grid.WorldToCell(currentlyConsideredLine.from);
|
|
1077
|
+
FastVector3Int to = grid.WorldToCell(currentlyConsideredLine.to);
|
|
1078
|
+
concaveHull.Add(from);
|
|
1079
|
+
concaveHull.Add(to);
|
|
1080
|
+
concaveHullLines.RemoveAtSwapBack(0);
|
|
1081
|
+
while (0 < concaveHullLines.Count)
|
|
1082
|
+
{
|
|
1083
|
+
int index = concaveHullLines.FindIndex(line =>
|
|
1084
|
+
{
|
|
1085
|
+
FastVector3Int lineFrom = grid.WorldToCell(line.from);
|
|
1086
|
+
return lineFrom == to;
|
|
1087
|
+
});
|
|
1088
|
+
|
|
1089
|
+
currentlyConsideredLine = concaveHullLines[index];
|
|
1090
|
+
to = grid.WorldToCell(currentlyConsideredLine.to);
|
|
1091
|
+
if (to == from)
|
|
1092
|
+
{
|
|
1093
|
+
break;
|
|
1094
|
+
}
|
|
1095
|
+
concaveHull.Add(to);
|
|
1096
|
+
concaveHullLines.RemoveAtSwapBack(index);
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
return concaveHull;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
public static bool IsPositionInside(
|
|
1103
|
+
List<FastVector3Int> hull,
|
|
1104
|
+
FastVector3Int gridPosition,
|
|
1105
|
+
Grid grid
|
|
1106
|
+
)
|
|
1107
|
+
{
|
|
1108
|
+
bool isPositionInside = false;
|
|
1109
|
+
Vector2 position = grid.CellToWorld(gridPosition);
|
|
1110
|
+
for (int i = 0; i < hull.Count; ++i)
|
|
1111
|
+
{
|
|
1112
|
+
Vector2 oldVector = grid.CellToWorld(hull[i]);
|
|
1113
|
+
int nextIndex = (i + 1) % hull.Count;
|
|
1114
|
+
Vector2 newVector = grid.CellToWorld(hull[nextIndex]);
|
|
1115
|
+
|
|
1116
|
+
Vector2 lhs;
|
|
1117
|
+
Vector2 rhs;
|
|
1118
|
+
if (oldVector.x < newVector.x)
|
|
1119
|
+
{
|
|
1120
|
+
lhs = oldVector;
|
|
1121
|
+
rhs = newVector;
|
|
1122
|
+
}
|
|
1123
|
+
else
|
|
1124
|
+
{
|
|
1125
|
+
lhs = newVector;
|
|
1126
|
+
rhs = oldVector;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
if (
|
|
1130
|
+
(newVector.x < position.x) == (position.x <= oldVector.x)
|
|
1131
|
+
&& (position.y - (long)lhs.y) * (rhs.x - lhs.x)
|
|
1132
|
+
< (rhs.y - (long)lhs.y) * (position.x - lhs.x)
|
|
1133
|
+
)
|
|
1134
|
+
{
|
|
1135
|
+
isPositionInside = !isPositionInside;
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
return isPositionInside;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
private static List<Line> GetDividedLine(
|
|
1143
|
+
Line line,
|
|
1144
|
+
IEnumerable<FastVector3Int> nearbyPoints,
|
|
1145
|
+
List<Line> concaveHull,
|
|
1146
|
+
Grid grid,
|
|
1147
|
+
float concavity
|
|
1148
|
+
)
|
|
1149
|
+
{
|
|
1150
|
+
return GetDividedLine(line.from, line.to, nearbyPoints, concaveHull, grid, concavity);
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
private static List<Line> GetDividedLine(
|
|
1154
|
+
Vector2 from,
|
|
1155
|
+
Vector2 to,
|
|
1156
|
+
IEnumerable<FastVector3Int> nearbyPoints,
|
|
1157
|
+
List<Line> concaveHull,
|
|
1158
|
+
Grid grid,
|
|
1159
|
+
float concavity
|
|
1160
|
+
)
|
|
1161
|
+
{
|
|
1162
|
+
List<Line> dividedLine = new(2);
|
|
1163
|
+
Dictionary<Vector2, double> okMiddlePoints = new();
|
|
1164
|
+
foreach (FastVector3Int gridPoint in nearbyPoints)
|
|
1165
|
+
{
|
|
1166
|
+
Vector2 point = grid.CellToWorld(gridPoint);
|
|
1167
|
+
double cosine = GetCosine(from, to, point);
|
|
1168
|
+
if (cosine < concavity)
|
|
1169
|
+
{
|
|
1170
|
+
Line newLineA = new(from, point);
|
|
1171
|
+
Line newLineB = new(point, to);
|
|
1172
|
+
if (
|
|
1173
|
+
!LineCollidesWithHull(newLineA, concaveHull)
|
|
1174
|
+
&& !LineCollidesWithHull(newLineB, concaveHull)
|
|
1175
|
+
)
|
|
1176
|
+
{
|
|
1177
|
+
okMiddlePoints[point] = cosine;
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
if (0 < okMiddlePoints.Count)
|
|
1183
|
+
{
|
|
1184
|
+
Vector2 middlePoint = new();
|
|
1185
|
+
double minCosine = double.MaxValue;
|
|
1186
|
+
foreach (KeyValuePair<Vector2, double> entry in okMiddlePoints)
|
|
1187
|
+
{
|
|
1188
|
+
double cosine = entry.Value;
|
|
1189
|
+
if (cosine < minCosine)
|
|
1190
|
+
{
|
|
1191
|
+
minCosine = cosine;
|
|
1192
|
+
middlePoint = entry.Key;
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
dividedLine.Add(new Line(from, middlePoint));
|
|
1197
|
+
dividedLine.Add(new Line(middlePoint, to));
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
return dividedLine;
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
private static bool LineCollidesWithHull(Line line, List<Line> concaveHull)
|
|
1204
|
+
{
|
|
1205
|
+
return LineCollidesWithHull(line.from, line.to, concaveHull);
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
private static bool LineCollidesWithHull(Vector2 from, Vector2 to, List<Line> concaveHull)
|
|
1209
|
+
{
|
|
1210
|
+
foreach (Line line in concaveHull)
|
|
1211
|
+
{
|
|
1212
|
+
Vector2 lhs = line.from;
|
|
1213
|
+
Vector2 rhs = line.to;
|
|
1214
|
+
|
|
1215
|
+
if (from != lhs && from != rhs && to != lhs && to != rhs)
|
|
1216
|
+
{
|
|
1217
|
+
if (Intersects(from, to, lhs, rhs))
|
|
1218
|
+
{
|
|
1219
|
+
return true;
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
return false;
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
public static double GetCosine(Vector2 a, Vector3 b, Vector3 o)
|
|
1228
|
+
{
|
|
1229
|
+
/* Law of cosines */
|
|
1230
|
+
double aPow2 = (a.x - o.x) * (a.x - o.x) + (a.y - o.y) * (a.y - o.y);
|
|
1231
|
+
double bPow2 = (b.x - o.x) * (b.x - o.x) + (b.y - o.y) * (b.y - o.y);
|
|
1232
|
+
double cPow2 = (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
|
|
1233
|
+
double cos = (aPow2 + bPow2 - cPow2) / (2 * Math.Sqrt(aPow2 * bPow2));
|
|
1234
|
+
return Math.Round(cos, 4);
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
private static IEnumerable<FastVector3Int> GetNearbyPoints(
|
|
1238
|
+
Line line,
|
|
1239
|
+
ICollection<FastVector3Int> points,
|
|
1240
|
+
Grid grid,
|
|
1241
|
+
float scaleFactor
|
|
1242
|
+
)
|
|
1243
|
+
{
|
|
1244
|
+
return GetNearbyPoints(line.from, line.to, points, grid, scaleFactor);
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
private static IEnumerable<FastVector3Int> GetNearbyPoints(
|
|
1248
|
+
Vector2 from,
|
|
1249
|
+
Vector2 to,
|
|
1250
|
+
ICollection<FastVector3Int> points,
|
|
1251
|
+
Grid grid,
|
|
1252
|
+
float scaleFactor
|
|
1253
|
+
)
|
|
1254
|
+
{
|
|
1255
|
+
const int maxTries = 2;
|
|
1256
|
+
for (int tries = 0; tries < maxTries; ++tries)
|
|
1257
|
+
{
|
|
1258
|
+
bool foundAnyPoints = false;
|
|
1259
|
+
Bounds boundary = GetBoundary(from, to, scaleFactor);
|
|
1260
|
+
foreach (FastVector3Int gridPoint in points)
|
|
1261
|
+
{
|
|
1262
|
+
Vector2 point = grid.CellToWorld(gridPoint);
|
|
1263
|
+
if (point != from && point != to)
|
|
1264
|
+
{
|
|
1265
|
+
if (boundary.FastContains2D(point))
|
|
1266
|
+
{
|
|
1267
|
+
foundAnyPoints = true;
|
|
1268
|
+
yield return gridPoint;
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
if (foundAnyPoints)
|
|
1274
|
+
{
|
|
1275
|
+
yield break;
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
scaleFactor *= (4 / 3f);
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
private static Bounds GetBoundary(Vector2 from, Vector2 to, float scaleFactor)
|
|
1283
|
+
{
|
|
1284
|
+
float xMin = Math.Min(from.x, to.x);
|
|
1285
|
+
float yMin = Math.Min(from.y, to.y);
|
|
1286
|
+
float xMax = Math.Max(from.x, to.x);
|
|
1287
|
+
float yMax = Math.Max(from.y, to.y);
|
|
1288
|
+
|
|
1289
|
+
float width = xMax - xMin;
|
|
1290
|
+
float height = yMax - yMin;
|
|
1291
|
+
return new Bounds(
|
|
1292
|
+
new Vector3(xMin + width / 2, yMin + height / 2),
|
|
1293
|
+
new Vector3(width, height) * scaleFactor + new Vector3(0.001f, 0.001f)
|
|
1294
|
+
);
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
// https://www.geeksforgeeks.org/how-to-check-if-a-given-point-lies-inside-a-polygon/#
|
|
1298
|
+
|
|
1299
|
+
/// <summary>
|
|
1300
|
+
/// Returns true if a line segment 'lhsFrom->lhsTo' intersects the line segment
|
|
1301
|
+
/// 'rhsFrom->rhsTo'
|
|
1302
|
+
/// </summary>
|
|
1303
|
+
/// <param name="lhsFrom">LineSegmentA start point.</param>
|
|
1304
|
+
/// <param name="lhsTo">LineSegmentA end point.</param>
|
|
1305
|
+
/// <param name="rhsFrom">LineSegmentB start point.</param>
|
|
1306
|
+
/// <param name="rhsTo">LineSegmentB end point.</param>
|
|
1307
|
+
/// <returns>True if the line segments intersect.</returns>
|
|
1308
|
+
public static bool Intersects(
|
|
1309
|
+
Vector2 lhsFrom,
|
|
1310
|
+
Vector2 lhsTo,
|
|
1311
|
+
Vector2 rhsFrom,
|
|
1312
|
+
Vector2 rhsTo
|
|
1313
|
+
)
|
|
1314
|
+
{
|
|
1315
|
+
if (lhsFrom == rhsFrom)
|
|
1316
|
+
{
|
|
1317
|
+
return false;
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
if (lhsFrom == rhsTo)
|
|
1321
|
+
{
|
|
1322
|
+
return false;
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
if (lhsTo == rhsFrom)
|
|
1326
|
+
{
|
|
1327
|
+
return false;
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
if (lhsTo == rhsTo)
|
|
1331
|
+
{
|
|
1332
|
+
return false;
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
OrientationType orientation1 = Orientation(lhsFrom, lhsTo, rhsFrom);
|
|
1336
|
+
OrientationType orientation2 = Orientation(lhsFrom, lhsTo, rhsTo);
|
|
1337
|
+
OrientationType orientation3 = Orientation(rhsFrom, rhsTo, lhsFrom);
|
|
1338
|
+
OrientationType orientation4 = Orientation(rhsFrom, rhsTo, lhsTo);
|
|
1339
|
+
|
|
1340
|
+
if (orientation1 != orientation2 && orientation3 != orientation4)
|
|
1341
|
+
{
|
|
1342
|
+
return true;
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
if (orientation1 == OrientationType.Colinear && LiesOnSegment(lhsFrom, rhsFrom, lhsTo))
|
|
1346
|
+
{
|
|
1347
|
+
return true;
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
if (orientation2 == OrientationType.Colinear && LiesOnSegment(lhsFrom, rhsTo, lhsTo))
|
|
1351
|
+
{
|
|
1352
|
+
return true;
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
if (orientation3 == OrientationType.Colinear && LiesOnSegment(rhsFrom, lhsFrom, rhsTo))
|
|
1356
|
+
{
|
|
1357
|
+
return true;
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
if (orientation4 == OrientationType.Colinear && LiesOnSegment(rhsFrom, lhsTo, rhsTo))
|
|
1361
|
+
{
|
|
1362
|
+
return true;
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
return false;
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
/// <summary>
|
|
1369
|
+
/// Given three colinear points p, q, r, returns whether the
|
|
1370
|
+
/// point q lines on the line segment pr.
|
|
1371
|
+
/// </summary>
|
|
1372
|
+
/// <param name="p">Beginning of line segment.</param>
|
|
1373
|
+
/// <param name="q">Check if on line segment.</param>
|
|
1374
|
+
/// <param name="r">End of line segment.</param>
|
|
1375
|
+
/// <returns>True if q lies on the line segment pr.</returns>
|
|
1376
|
+
public static bool LiesOnSegment(Vector2 p, Vector2 q, Vector2 r)
|
|
1377
|
+
{
|
|
1378
|
+
return q.x <= Math.Max(p.x, r.x)
|
|
1379
|
+
&& Math.Min(p.x, r.x) <= q.x
|
|
1380
|
+
&& q.y <= Math.Max(p.y, r.y)
|
|
1381
|
+
&& Math.Min(p.y, r.y) <= q.y;
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
public enum OrientationType
|
|
1385
|
+
{
|
|
1386
|
+
Colinear = 0,
|
|
1387
|
+
Clockwise = 1,
|
|
1388
|
+
Counterclockwise = 2,
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
/// <summary>
|
|
1392
|
+
/// Finds the orientation of an ordered triplet (p, q, r).
|
|
1393
|
+
/// </summary>
|
|
1394
|
+
/// <param name="p">Triplet element 1.</param>
|
|
1395
|
+
/// <param name="q">Triplet element 2.</param>
|
|
1396
|
+
/// <param name="r">Triplet element 3.</param>
|
|
1397
|
+
/// <returns>The orientation of the triplet</returns>
|
|
1398
|
+
public static OrientationType Orientation(Vector2 p, Vector2 q, Vector2 r)
|
|
1399
|
+
{
|
|
1400
|
+
float value = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
|
|
1401
|
+
if (Mathf.Approximately(value, 0))
|
|
1402
|
+
{
|
|
1403
|
+
return OrientationType.Colinear;
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
return 0 < value ? OrientationType.Clockwise : OrientationType.Counterclockwise;
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
#endregion
|
|
1410
|
+
|
|
1411
|
+
public static Vector2 Rotate(this Vector2 v, float degrees)
|
|
1412
|
+
{
|
|
1413
|
+
float sin = Mathf.Sin(degrees * Mathf.Deg2Rad);
|
|
1414
|
+
float cos = Mathf.Cos(degrees * Mathf.Deg2Rad);
|
|
1415
|
+
|
|
1416
|
+
float tx = v.x;
|
|
1417
|
+
float ty = v.y;
|
|
1418
|
+
|
|
1419
|
+
Vector2 rotatedVector;
|
|
1420
|
+
rotatedVector.x = (cos * tx) - (sin * ty);
|
|
1421
|
+
rotatedVector.y = (sin * tx) + (cos * ty);
|
|
1422
|
+
|
|
1423
|
+
return rotatedVector;
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
public static bool FastIntersects(this Bounds bounds, Bounds other)
|
|
1427
|
+
{
|
|
1428
|
+
Vector3 boundsMin = bounds.min;
|
|
1429
|
+
Vector3 otherMax = other.max;
|
|
1430
|
+
if (otherMax.x < boundsMin.x || otherMax.y < boundsMin.y || otherMax.z < boundsMin.z)
|
|
1431
|
+
{
|
|
1432
|
+
return false;
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
Vector3 boundsMax = bounds.max;
|
|
1436
|
+
Vector3 otherMin = other.min;
|
|
1437
|
+
return boundsMax.x >= otherMin.x
|
|
1438
|
+
&& boundsMax.y >= otherMin.y
|
|
1439
|
+
&& boundsMax.z >= otherMin.z;
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
public static bool FastContains2D(this BoundsInt bounds, FastVector3Int position)
|
|
1443
|
+
{
|
|
1444
|
+
return position.x >= bounds.xMin
|
|
1445
|
+
&& position.y >= bounds.yMin
|
|
1446
|
+
&& position.x < bounds.xMax
|
|
1447
|
+
&& position.y < bounds.yMax;
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
public static bool FastIntersects2D(this BoundsInt bounds, BoundsInt other)
|
|
1451
|
+
{
|
|
1452
|
+
if (other.xMax < bounds.xMin || other.yMax < bounds.yMin)
|
|
1453
|
+
{
|
|
1454
|
+
return false;
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
return bounds.xMax >= other.xMin && bounds.yMax >= other.yMin;
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
public static bool FastContains2D(this Bounds bounds, Vector2 position)
|
|
1461
|
+
{
|
|
1462
|
+
Vector3 min = bounds.min;
|
|
1463
|
+
if (position.x < min.x || position.y < bounds.min.y)
|
|
1464
|
+
{
|
|
1465
|
+
return false;
|
|
1466
|
+
}
|
|
1467
|
+
Vector3 max = bounds.max;
|
|
1468
|
+
return position.x < max.x && position.y < max.y;
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
public static bool FastIntersects2D(this Bounds bounds, Bounds other)
|
|
1472
|
+
{
|
|
1473
|
+
Vector3 boundsMin = bounds.min;
|
|
1474
|
+
Vector3 otherMax = other.max;
|
|
1475
|
+
if (otherMax.x < boundsMin.x || otherMax.y < boundsMin.y)
|
|
1476
|
+
{
|
|
1477
|
+
return false;
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
Vector3 boundsMax = bounds.max;
|
|
1481
|
+
Vector3 otherMin = other.min;
|
|
1482
|
+
return boundsMax.x >= otherMin.x && boundsMax.y >= otherMin.y;
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
public static bool Overlaps2D(this Bounds bounds, Bounds other)
|
|
1486
|
+
{
|
|
1487
|
+
Vector3 boundsMin = bounds.min;
|
|
1488
|
+
Vector3 otherMin = other.min;
|
|
1489
|
+
if (otherMin.x < boundsMin.x || otherMin.y < boundsMin.y)
|
|
1490
|
+
{
|
|
1491
|
+
return false;
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
Vector3 boundsMax = bounds.max;
|
|
1495
|
+
Vector3 otherMax = other.max;
|
|
1496
|
+
return otherMax.x <= boundsMax.x && otherMax.y <= boundsMax.y;
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
public static BoundsInt WithPadding(this BoundsInt bounds, int xPadding, int yPadding)
|
|
1500
|
+
{
|
|
1501
|
+
Vector3Int size = bounds.size;
|
|
1502
|
+
return new BoundsInt(
|
|
1503
|
+
bounds.xMin - xPadding,
|
|
1504
|
+
bounds.yMin - yPadding,
|
|
1505
|
+
bounds.zMin,
|
|
1506
|
+
size.x + 2 * xPadding,
|
|
1507
|
+
size.y + 2 * yPadding,
|
|
1508
|
+
size.z
|
|
1509
|
+
);
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
public static void SetColors(this UnityEngine.UI.Slider slider, Color color)
|
|
1513
|
+
{
|
|
1514
|
+
ColorBlock block = slider.colors;
|
|
1515
|
+
|
|
1516
|
+
block.normalColor = color;
|
|
1517
|
+
block.highlightedColor = color;
|
|
1518
|
+
block.pressedColor = color;
|
|
1519
|
+
block.selectedColor = color;
|
|
1520
|
+
block.disabledColor = color;
|
|
1521
|
+
|
|
1522
|
+
slider.colors = block;
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
public static void SetLeft(this RectTransform rt, float left)
|
|
1526
|
+
{
|
|
1527
|
+
rt.offsetMin = new Vector2(left, rt.offsetMin.y);
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
public static void SetRight(this RectTransform rt, float right)
|
|
1531
|
+
{
|
|
1532
|
+
rt.offsetMax = new Vector2(-right, rt.offsetMax.y);
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
public static void SetTop(this RectTransform rt, float top)
|
|
1536
|
+
{
|
|
1537
|
+
rt.offsetMax = new Vector2(rt.offsetMax.x, -top);
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
public static void SetBottom(this RectTransform rt, float bottom)
|
|
1541
|
+
{
|
|
1542
|
+
rt.offsetMin = new Vector2(rt.offsetMin.x, bottom);
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
public static IEnumerable<FastVector3Int> AllFastPositionsWithin(this BoundsInt bounds)
|
|
1546
|
+
{
|
|
1547
|
+
Vector3Int min = bounds.min;
|
|
1548
|
+
Vector3Int max = bounds.max;
|
|
1549
|
+
for (int x = min.x; x < max.x; ++x)
|
|
1550
|
+
{
|
|
1551
|
+
for (int y = min.y; y < max.y; ++y)
|
|
1552
|
+
{
|
|
1553
|
+
for (int z = min.z; z < max.z; ++z)
|
|
1554
|
+
{
|
|
1555
|
+
yield return new FastVector3Int(x, y, z);
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
public static bool Contains(this BoundsInt bounds, FastVector3Int position)
|
|
1562
|
+
{
|
|
1563
|
+
return bounds.Contains(position);
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
public static bool IsOnEdge2D(this FastVector3Int position, BoundsInt bounds)
|
|
1567
|
+
{
|
|
1568
|
+
if (bounds.xMin == position.x || (bounds.xMax - 1) == position.x)
|
|
1569
|
+
{
|
|
1570
|
+
return bounds.yMin <= position.y && position.y < bounds.yMax;
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
if (bounds.yMin == position.y || (bounds.yMax - 1) == position.y)
|
|
1574
|
+
{
|
|
1575
|
+
return bounds.xMin <= position.x && position.x < bounds.xMax;
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
return false;
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
#if UNITY_EDITOR
|
|
1582
|
+
public static IEnumerable<Sprite> GetSpritesFromClip(this AnimationClip clip)
|
|
1583
|
+
{
|
|
1584
|
+
if (clip == null)
|
|
1585
|
+
{
|
|
1586
|
+
yield break;
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
foreach (
|
|
1590
|
+
EditorCurveBinding binding in AnimationUtility.GetObjectReferenceCurveBindings(clip)
|
|
1591
|
+
)
|
|
1592
|
+
{
|
|
1593
|
+
ObjectReferenceKeyframe[] keyframes = AnimationUtility.GetObjectReferenceCurve(
|
|
1594
|
+
clip,
|
|
1595
|
+
binding
|
|
1596
|
+
);
|
|
1597
|
+
foreach (ObjectReferenceKeyframe frame in keyframes)
|
|
1598
|
+
{
|
|
1599
|
+
if (frame.value is Sprite sprite)
|
|
1600
|
+
{
|
|
1601
|
+
yield return sprite;
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
#endif
|
|
1607
|
+
}
|
|
1608
|
+
}
|