diff --git a/Assets/BasePlanet.prefab b/Assets/BasePlanet.prefab index 64cf1e7..b28ad14 100644 --- a/Assets/BasePlanet.prefab +++ b/Assets/BasePlanet.prefab @@ -49,7 +49,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 5e5a2958b00708d4bacd5703ef7b2b50, type: 3} m_Name: m_EditorClassIdentifier: - Size: 139 + Size: 143 Bulbs: 1 edgeColor: {r: 0.4811321, g: 0.4811321, b: 0.4811321, a: 1} fillColor: {r: 0.1509434, g: 0.1509434, b: 0.1509434, a: 1} @@ -97,115 +97,117 @@ LineRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_Positions: - - {x: 1.39, y: 0, z: 0} - - {x: 1.3876913, y: 0.08008066, z: 0} - - {x: 1.3807727, y: 0.1598953, z: 0} - - {x: 1.3692675, y: 0.23917878, z: 0} - - {x: 1.3532137, y: 0.31766775, z: 0} - - {x: 1.3326646, y: 0.39510143, z: 0} - - {x: 1.3076885, y: 0.47122264, z: 0} - - {x: 1.2783685, y: 0.5457785, z: 0} - - {x: 1.2448018, y: 0.61852133, z: 0} - - {x: 1.2070999, y: 0.6892095, z: 0} - - {x: 1.1653882, y: 0.7576082, z: 0} - - {x: 1.1198053, y: 0.8234901, z: 0} - - {x: 1.0705024, y: 0.88663656, z: 0} - - {x: 1.0176436, y: 0.9468377, z: 0} - - {x: 0.9614041, y: 1.0038935, z: 0} - - {x: 0.9019709, y: 1.0576144, z: 0} - - {x: 0.8395415, y: 1.1078222, z: 0} - - {x: 0.7743233, y: 1.1543498, z: 0} - - {x: 0.7065328, y: 1.1970428, z: 0} - - {x: 0.6363952, y: 1.2357593, z: 0} - - {x: 0.5641437, y: 1.2703707, z: 0} - - {x: 0.4900181, y: 1.3007622, z: 0} - - {x: 0.41426474, y: 1.3268325, z: 0} - - {x: 0.33713508, y: 1.3484954, z: 0} - - {x: 0.25888562, y: 1.3656787, z: 0} - - {x: 0.17977619, y: 1.3783252, z: 0} - - {x: 0.10006955, y: 1.3863932, z: 0} - - {x: 0.020030497, y: 1.3898556, z: 0} - - {x: -0.060075097, y: 1.3887011, z: 0} - - {x: -0.13998112, y: 1.3829336, z: 0} - - {x: -0.21942216, y: 1.372572, z: 0} - - {x: -0.2981343, y: 1.3576509, z: 0} - - {x: -0.37585622, y: 1.3382198, z: 0} - - {x: -0.4523294, y: 1.3143432, z: 0} - - {x: -0.5273, y: 1.2861005, z: 0} - - {x: -0.600519, y: 1.2535856, z: 0} - - {x: -0.6717431, y: 1.2169064, z: 0} - - {x: -0.74073577, y: 1.1761848, z: 0} - - {x: -0.8072677, y: 1.1315559, z: 0} - - {x: -0.8711181, y: 1.0831681, z: 0} - - {x: -0.93207467, y: 1.0311822, z: 0} - - {x: -0.9899349, y: 0.97577083, z: 0} - - {x: -1.0445068, y: 0.917118, z: 0} - - {x: -1.095609, y: 0.8554186, z: 0} - - {x: -1.1430715, y: 0.7908776, z: 0} - - {x: -1.186737, y: 0.7237094, z: 0} - - {x: -1.2264603, y: 0.65413684, z: 0} - - {x: -1.2621094, y: 0.58239156, z: 0} - - {x: -1.2935658, y: 0.50871164, z: 0} - - {x: -1.3207252, y: 0.4333418, z: 0} - - {x: -1.3434972, y: 0.35653248, z: 0} - - {x: -1.3618063, y: 0.27853876, z: 0} - - {x: -1.3755915, y: 0.19961978, z: 0} - - {x: -1.3848071, y: 0.12003768, z: 0} - - {x: -1.3894227, y: 0.040056836, z: 0} - - {x: -1.3894227, y: -0.040057078, z: 0} - - {x: -1.3848071, y: -0.12003792, z: 0} - - {x: -1.3755915, y: -0.19962001, z: 0} - - {x: -1.3618062, y: -0.278539, z: 0} - - {x: -1.3434972, y: -0.3565327, z: 0} - - {x: -1.3207251, y: -0.433342, z: 0} - - {x: -1.2935658, y: -0.5087119, z: 0} - - {x: -1.2621093, y: -0.58239174, z: 0} - - {x: -1.2264602, y: -0.6541371, z: 0} - - {x: -1.1867368, y: -0.72370964, z: 0} - - {x: -1.1430714, y: -0.7908779, z: 0} - - {x: -1.0956088, y: -0.85541886, z: 0} - - {x: -1.0445067, y: -0.9171182, z: 0} - - {x: -0.98993474, y: -0.975771, z: 0} - - {x: -0.9320745, y: -1.0311824, z: 0} - - {x: -0.87111783, y: -1.0831683, z: 0} - - {x: -0.8072676, y: -1.131556, z: 0} - - {x: -0.74073553, y: -1.1761848, z: 0} - - {x: -0.67174286, y: -1.2169065, z: 0} - - {x: -0.60051876, y: -1.2535857, z: 0} - - {x: -0.52729976, y: -1.2861006, z: 0} - - {x: -0.4523292, y: -1.3143432, z: 0} - - {x: -0.37585598, y: -1.3382198, z: 0} - - {x: -0.2981342, y: -1.3576509, z: 0} - - {x: -0.21942207, y: -1.372572, z: 0} - - {x: -0.13998105, y: -1.3829336, z: 0} - - {x: -0.060075022, y: -1.3887011, z: 0} - - {x: 0.020030575, y: -1.3898556, z: 0} - - {x: 0.10006963, y: -1.3863932, z: 0} - - {x: 0.17977627, y: -1.3783252, z: 0} - - {x: 0.2588857, y: -1.3656787, z: 0} - - {x: 0.33713514, y: -1.3484954, z: 0} - - {x: 0.41426465, y: -1.3268325, z: 0} - - {x: 0.490018, y: -1.3007622, z: 0} - - {x: 0.5641436, y: -1.2703708, z: 0} - - {x: 0.63639516, y: -1.2357594, z: 0} - - {x: 0.70653325, y: -1.1970425, z: 0} - - {x: 0.77432376, y: -1.1543496, z: 0} - - {x: 0.839542, y: -1.1078218, z: 0} - - {x: 0.9019713, y: -1.0576142, z: 0} - - {x: 0.9614044, y: -1.0038931, z: 0} - - {x: 1.0176438, y: -0.94683737, z: 0} - - {x: 1.0705028, y: -0.88663626, z: 0} - - {x: 1.1198056, y: -0.8234898, z: 0} - - {x: 1.1653885, y: -0.7576078, z: 0} - - {x: 1.2071002, y: -0.6892091, z: 0} - - {x: 1.2448019, y: -0.618521, z: 0} - - {x: 1.2783685, y: -0.54577816, z: 0} - - {x: 1.3076886, y: -0.4712223, z: 0} - - {x: 1.3326646, y: -0.3951011, z: 0} - - {x: 1.3532137, y: -0.31766742, z: 0} - - {x: 1.3692676, y: -0.2391785, z: 0} - - {x: 1.3807728, y: -0.15989502, z: 0} - - {x: 1.3876913, y: -0.0800804, z: 0} + - {x: 1.43, y: 0, z: 0} + - {x: 1.4277096, y: 0.08090232, z: 0} + - {x: 1.4208459, y: 0.16154549, z: 0} + - {x: 1.4094307, y: 0.24167116, z: 0} + - {x: 1.3935007, y: 0.32102272, z: 0} + - {x: 1.373107, y: 0.3993459, z: 0} + - {x: 1.3483148, y: 0.4763899, z: 0} + - {x: 1.3192034, y: 0.5519079, z: 0} + - {x: 1.2858663, y: 0.6256579, z: 0} + - {x: 1.2484101, y: 0.6974037, z: 0} + - {x: 1.206955, y: 0.7669156, z: 0} + - {x: 1.1616336, y: 0.83397084, z: 0} + - {x: 1.1125911, y: 0.8983546, z: 0} + - {x: 1.0599846, y: 0.9598606, z: 0} + - {x: 1.0039828, y: 1.018292, z: 0} + - {x: 0.9447648, y: 1.0734614, z: 0} + - {x: 0.8825205, y: 1.1251922, z: 0} + - {x: 0.8174492, y: 1.1733186, z: 0} + - {x: 0.74975944, y: 1.2176867, z: 0} + - {x: 0.6796678, y: 1.258154, z: 0} + - {x: 0.6073991, y: 1.2945911, z: 0} + - {x: 0.53318465, y: 1.3268813, z: 0} + - {x: 0.45726237, y: 1.354921, z: 0} + - {x: 0.37987518, y: 1.3786206, z: 0} + - {x: 0.3012713, y: 1.397904, z: 0} + - {x: 0.22170235, y: 1.4127095, z: 0} + - {x: 0.14142308, y: 1.4229896, z: 0} + - {x: 0.060690925, y: 1.4287114, z: 0} + - {x: -0.020235796, y: 1.4298568, z: 0} + - {x: -0.10109753, y: 1.4264218, z: 0} + - {x: -0.18163557, y: 1.4184176, z: 0} + - {x: -0.2615916, y: 1.4058697, z: 0} + - {x: -0.3407099, y: 1.3888184, z: 0} + - {x: -0.4187366, y: 1.3673184, z: 0} + - {x: -0.49542212, y: 1.3414383, z: 0} + - {x: -0.57052046, y: 1.3112613, z: 0} + - {x: -0.6437913, y: 1.276884, z: 0} + - {x: -0.71500003, y: 1.2384163, z: 0} + - {x: -0.7839184, y: 1.1959815, z: 0} + - {x: -0.8503254, y: 1.1497159, z: 0} + - {x: -0.9140087, y: 1.0997672, z: 0} + - {x: -0.9747643, y: 1.0462956, z: 0} + - {x: -1.0323973, y: 0.9894724, z: 0} + - {x: -1.0867231, y: 0.9294799, z: 0} + - {x: -1.1375679, y: 0.8665097, z: 0} + - {x: -1.1847688, y: 0.8007637, z: 0} + - {x: -1.2281746, y: 0.7324528, z: 0} + - {x: -1.2676458, y: 0.66179585, z: 0} + - {x: -1.3030567, y: 0.58901864, z: 0} + - {x: -1.3342935, y: 0.5143547, z: 0} + - {x: -1.361256, y: 0.4380434, z: 0} + - {x: -1.3838581, y: 0.36032858, z: 0} + - {x: -1.4020272, y: 0.2814595, z: 0} + - {x: -1.4157052, y: 0.20168887, z: 0} + - {x: -1.4248483, y: 0.12127248, z: 0} + - {x: -1.4294273, y: 0.04046729, z: 0} + - {x: -1.4294273, y: -0.040467538, z: 0} + - {x: -1.4248483, y: -0.12127273, z: 0} + - {x: -1.4157052, y: -0.20168911, z: 0} + - {x: -1.4020272, y: -0.28145975, z: 0} + - {x: -1.383858, y: -0.3603288, z: 0} + - {x: -1.3612559, y: -0.43804362, z: 0} + - {x: -1.3342934, y: -0.5143549, z: 0} + - {x: -1.3030566, y: -0.58901894, z: 0} + - {x: -1.2676457, y: -0.6617961, z: 0} + - {x: -1.2281744, y: -0.73245305, z: 0} + - {x: -1.1847687, y: -0.800764, z: 0} + - {x: -1.1375679, y: -0.86650985, z: 0} + - {x: -1.0867229, y: -0.9294801, z: 0} + - {x: -1.0323972, y: -0.98947257, z: 0} + - {x: -0.97476405, y: -1.0462958, z: 0} + - {x: -0.9140088, y: -1.0997671, z: 0} + - {x: -0.8503254, y: -1.1497158, z: 0} + - {x: -0.7839182, y: -1.1959817, z: 0} + - {x: -0.71499985, y: -1.2384163, z: 0} + - {x: -0.6437911, y: -1.276884, z: 0} + - {x: -0.5705201, y: -1.3112614, z: 0} + - {x: -0.49542156, y: -1.3414385, z: 0} + - {x: -0.4187367, y: -1.3673183, z: 0} + - {x: -0.3407098, y: -1.3888184, z: 0} + - {x: -0.26159155, y: -1.4058697, z: 0} + - {x: -0.18163534, y: -1.4184176, z: 0} + - {x: -0.10109727, y: -1.4264218, z: 0} + - {x: -0.020235375, y: -1.4298568, z: 0} + - {x: 0.060691345, y: -1.4287114, z: 0} + - {x: 0.14142366, y: -1.4229896, z: 0} + - {x: 0.22170226, y: -1.4127095, z: 0} + - {x: 0.30127138, y: -1.3979039, z: 0} + - {x: 0.3798754, y: -1.3786205, z: 0} + - {x: 0.4572626, y: -1.354921, z: 0} + - {x: 0.533185, y: -1.3268812, z: 0} + - {x: 0.6073995, y: -1.294591, z: 0} + - {x: 0.6796683, y: -1.2581538, z: 0} + - {x: 0.7497593, y: -1.2176867, z: 0} + - {x: 0.8174492, y: -1.1733186, z: 0} + - {x: 0.88252056, y: -1.125192, z: 0} + - {x: 0.944765, y: -1.0734613, z: 0} + - {x: 1.003983, y: -1.0182917, z: 0} + - {x: 1.0599849, y: -0.9598603, z: 0} + - {x: 1.1125914, y: -0.8983542, z: 0} + - {x: 1.1616335, y: -0.8339709, z: 0} + - {x: 1.206955, y: -0.7669156, z: 0} + - {x: 1.2484102, y: -0.69740367, z: 0} + - {x: 1.2858664, y: -0.6256577, z: 0} + - {x: 1.3192035, y: -0.5519076, z: 0} + - {x: 1.3483148, y: -0.47638956, z: 0} + - {x: 1.3731071, y: -0.39934546, z: 0} + - {x: 1.3935008, y: -0.32102215, z: 0} + - {x: 1.4094307, y: -0.2416712, z: 0} + - {x: 1.4208459, y: -0.16154543, z: 0} + - {x: 1.4277096, y: -0.08090216, z: 0} m_Parameters: serializedVersion: 3 widthMultiplier: 0.0466 @@ -262,7 +264,7 @@ LineRenderer: shadowBias: 0.5 generateLightingData: 0 m_MaskInteraction: 0 - m_UseWorldSpace: 1 + m_UseWorldSpace: 0 m_Loop: 1 m_ApplyActiveColorSpace: 1 --- !u!33 &-945589377389130640 @@ -349,7 +351,7 @@ CircleCollider2D: m_UsedByComposite: 0 m_Offset: {x: 0.001685977, y: 0} serializedVersion: 2 - m_Radius: 1.39 + m_Radius: 1.43 --- !u!114 &1236946940358429026 MonoBehaviour: m_ObjectHideFlags: 0 @@ -367,7 +369,7 @@ MonoBehaviour: m_BlendStyleIndex: 0 m_FalloffIntensity: 0.159 m_Color: {r: 1, g: 1, b: 1, a: 1} - m_Intensity: 1.57 + m_Intensity: 24.54 m_LightVolumeIntensity: 1 m_LightVolumeIntensityEnabled: 0 m_ApplyToSortingLayers: @@ -388,8 +390,8 @@ MonoBehaviour: m_Extent: {x: 0.9985302, y: 0.99853027, z: 0} m_PointLightInnerAngle: 360 m_PointLightOuterAngle: 360 - m_PointLightInnerRadius: 0 - m_PointLightOuterRadius: 1 + m_PointLightInnerRadius: 1 + m_PointLightOuterRadius: 2 m_ShapeLightParametricSides: 5 m_ShapeLightParametricAngleOffset: 0 m_ShapeLightParametricRadius: 1 diff --git a/Assets/Datastructures.meta b/Assets/Datastructures.meta new file mode 100644 index 0000000..f569347 --- /dev/null +++ b/Assets/Datastructures.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fd0fb2105300f73429e78907962749e2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Datastructures/Heap.meta b/Assets/Datastructures/Heap.meta new file mode 100644 index 0000000..7e41cf5 --- /dev/null +++ b/Assets/Datastructures/Heap.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5643fe3e36c9cef4cb65fd43995792a4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Datastructures/Heap/BaseHeap.cs b/Assets/Datastructures/Heap/BaseHeap.cs new file mode 100644 index 0000000..3707cf7 --- /dev/null +++ b/Assets/Datastructures/Heap/BaseHeap.cs @@ -0,0 +1,211 @@ +/*MIT License + +Copyright(c) 2018 Vili Volčini / viliwonka + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +using System.Collections.Generic; + +namespace DataStructures.ViliWonka.Heap { + + // array start at index 1, optimisation reason + public abstract class BaseHeap { + + protected int nodesCount; + protected int maxSize; + + protected float[] heap; + + protected BaseHeap(int initialSize) { + + maxSize = initialSize; + heap = new float[initialSize + 1]; + } + + public int Count { get { return nodesCount; } } + + public float HeadValue { get { return heap[1]; } } + + public void Clear() { + nodesCount = 0; + } + + protected int Parent(int index) { return (index >> 1); } + protected int Left (int index) { return (index << 1); } + protected int Right (int index) { return (index << 1) | 1; } + + // bubble down, MaxHeap version + protected void BubbleDownMax(int index) { + + int L = Left(index); + int R = Right(index); + + // bubbling down, 2 kids + while (R <= nodesCount) { + + // if heap property is violated between index and Left child + if(heap[index] < heap[L]) { + + if (heap[L] < heap[R]) { + + Swap(index, R); // left has bigger priority + index = R; + } + else { + + Swap(index, L); // right has bigger priority + index = L; + } + } + else { + // if heap property is violated between index and R + if (heap[index] < heap[R]) { + + Swap(index, R); + index = R; + } + else { + + index = L; + L = Left(index); + break; + } + + } + + L = Left(index); + R = Right(index); + } + + // only left & last children available to test and swap + if (L <= nodesCount && heap[index] < heap[L]) { + Swap(index, L); + } + } + + // bubble up, MaxHeap version + protected void BubbleUpMax(int index) { + + int P = Parent(index); + + //swap, until Heap property isn't violated anymore + while (P > 0 && heap[P] < heap[index]) { + + Swap(P, index); + + index = P; + P = Parent(index); + } + } + + // bubble down, MinHeap version + protected void BubbleDownMin(int index) { + + int L = Left(index); + int R = Right(index); + + // bubbling down, 2 kids + while(R <= nodesCount) { + + // if heap property is violated between index and Left child + if(heap[index] > heap[L]) { + + if(heap[L] > heap[R]) { + + Swap(index, R); // right has smaller priority + index = R; + } + else { + + Swap(index, L); // left has smaller priority + index = L; + } + } + else { + // if heap property is violated between index and R + if(heap[index] > heap[R]) { + + Swap(index, R); + index = R; + } + else { + + index = L; + L = Left(index); + break; + } + + } + + L = Left(index); + R = Right(index); + } + + // only left & last children available to test and swap + if(L <= nodesCount && heap[index] > heap[L]) { + Swap(index, L); + } + } + + // bubble up, MinHeap version + protected void BubbleUpMin(int index) { + + int P = Parent(index); + + //swap, until Heap property isn't violated anymore + while(P > 0 && heap[P] > heap[index]) { + + Swap(P, index); + + index = P; + P = Parent(index); + } + } + + protected float tempHeap; + protected virtual void Swap(int A, int B) { + + tempHeap = heap[A]; + heap[A] = heap[B]; + heap[B] = tempHeap; + } + + protected virtual void UpsizeHeap() { + + maxSize *= 2; + System.Array.Resize(ref heap, maxSize + 1); + } + + public virtual void PushValue(float h) { + throw new System.NotImplementedException(); + } + + public virtual float PopValue() { + throw new System.NotImplementedException(); + } + + public void FlushHeapResult(List heapList) { + + for(int i = 1; i < Count; i++) { + heapList.Add(heap[i]); + } + } + } +} \ No newline at end of file diff --git a/Assets/Datastructures/Heap/BaseHeap.cs.meta b/Assets/Datastructures/Heap/BaseHeap.cs.meta new file mode 100644 index 0000000..5b685b6 --- /dev/null +++ b/Assets/Datastructures/Heap/BaseHeap.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f79974e0b20985b47a168ebcb1f3e74b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Datastructures/Heap/KSmallest.cs b/Assets/Datastructures/Heap/KSmallest.cs new file mode 100644 index 0000000..e943045 --- /dev/null +++ b/Assets/Datastructures/Heap/KSmallest.cs @@ -0,0 +1,229 @@ +/*MIT License + +Copyright(c) 2018 Vili Volčini / viliwonka + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +using System.Collections.Generic; + +namespace DataStructures.ViliWonka.Heap { + + public class KSmallestHeap : BaseHeap { + + public KSmallestHeap(int maxEntries) : base(maxEntries) { + + } + + public bool Full { + get { + return maxSize == nodesCount; + } + } + + // in lots of cases, max head gets removed + public override void PushValue(float h) { + + // if heap full + if(nodesCount == maxSize) { + + // if Heads priority is smaller than input priority, then ignore that item + if(HeadValue < h) { + + return; + } + else { + + heap[1] = h; // remove top element + BubbleDownMax(1); // bubble it down + } + } + else { + + nodesCount++; + heap[nodesCount] = h; + BubbleUpMax(nodesCount); + } + } + + public override float PopValue() { + + if(nodesCount == 0) + throw new System.ArgumentException("Heap is empty!"); + + float result = heap[1]; + + heap[1] = heap[nodesCount]; + nodesCount--; + BubbleDownMax(1); + + return result; + } + + public void Print() { + + UnityEngine.Debug.Log("HeapPropertyHolds? " + HeapPropertyHolds(1)); + } + + //should remove + public bool HeapPropertyHolds(int index, int depth = 0) { + + if (index > nodesCount) + return true; + + UnityEngine.Debug.Log(heap[index]); + + int L = Left(index); + int R = Right(index); + + bool bothHold = true; + + if(L <= nodesCount) { + + UnityEngine.Debug.Log(heap[index] + " => " + heap[L]); + + if (heap[index] < heap[L]) + bothHold = false; + } + + // if L <= nodesCount, then R <= nodesCount can also happen + if (R <= nodesCount) { + + UnityEngine.Debug.Log(heap[index] + " => " + heap[R]); + + if (bothHold && heap[index] < heap[R]) + bothHold = false; + + } + + return bothHold & HeapPropertyHolds(L, depth + 1) & HeapPropertyHolds(R, depth + 1); + } + + } + + // array start at index 1 + // generic version + public class KSmallestHeap : KSmallestHeap { + + T[] objs; //objects + + public KSmallestHeap(int maxEntries) : base(maxEntries) { + objs = new T[maxEntries + 1]; + } + + public T HeadHeapObject { get { return objs[1]; } } + + T tempObjs; + protected override void Swap(int A, int B) { + + tempHeap = heap[A]; + tempObjs = objs[A]; + + heap[A] = heap[B]; + objs[A] = objs[B]; + + heap[B] = tempHeap; + objs[B] = tempObjs; + } + + public override void PushValue(float h) { + throw new System.ArgumentException("Use Push(T, float)!"); + } + + public void PushObj(T obj, float h) { + + // if heap full + if(nodesCount == maxSize) { + + // if Heads priority is smaller than input priority, then ignore that item + if(HeadValue < h) { + + return; + } + else { + + heap[1] = h; // remove top element + objs[1] = obj; + BubbleDownMax(1); // bubble it down + } + } + else { + + nodesCount++; + heap[nodesCount] = h; + objs[nodesCount] = obj; + BubbleUpMax(nodesCount); + } + } + + public override float PopValue() { + throw new System.ArgumentException("Use PopObj()!"); + } + + public T PopObj() { + + if(nodesCount == 0) + throw new System.ArgumentException("Heap is empty!"); + + T result = objs[1]; + + heap[1] = heap[nodesCount]; + objs[1] = objs[nodesCount]; + + nodesCount--; + BubbleDownMax(1); + + return result; + } + + public T PopObj(ref float heapValue) { + + if(nodesCount == 0) + throw new System.ArgumentException("Heap is empty!"); + + heapValue = heap[1]; + T result = PopObj(); + + return result; + } + + //flush internal results, returns ordered data + public void FlushResult(List resultList, List heapList = null) { + + int count = nodesCount + 1; + + + if(heapList == null) { + + for(int i = 1; i < count; i++) { + resultList.Add(PopObj()); + } + } + else { + + float h = 0f; + + for(int i = 1; i < count; i++) { + resultList.Add(PopObj(ref h)); + heapList.Add(h); + } + } + } + } +} \ No newline at end of file diff --git a/Assets/Datastructures/Heap/KSmallest.cs.meta b/Assets/Datastructures/Heap/KSmallest.cs.meta new file mode 100644 index 0000000..3a2ba31 --- /dev/null +++ b/Assets/Datastructures/Heap/KSmallest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 616a0666a4955ed4bbe7fcac27b32ae7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Datastructures/Heap/MaxHeap.cs b/Assets/Datastructures/Heap/MaxHeap.cs new file mode 100644 index 0000000..63c3c76 --- /dev/null +++ b/Assets/Datastructures/Heap/MaxHeap.cs @@ -0,0 +1,168 @@ +/*MIT License + +Copyright(c) 2018 Vili Volčini / viliwonka + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +using System.Collections.Generic; + +namespace DataStructures.ViliWonka.Heap { + + public class MaxHeap : BaseHeap { + + public MaxHeap(int initialSize = 2048) : base(initialSize) { + + + } + + public override void PushValue(float h) { + + // if heap array is full + if(nodesCount == maxSize) { + + UpsizeHeap(); + } + + nodesCount++; + heap[nodesCount] = h; + BubbleUpMax(nodesCount); + } + + public override float PopValue() { + + if(nodesCount == 0) + throw new System.ArgumentException("Heap is empty!"); + + float result = heap[1]; + + heap[1] = heap[nodesCount]; + nodesCount--; + BubbleDownMax(1); + + return result; + } + } + + // generic version + public class MaxHeap : MaxHeap { + + T[] objs; // objects + + public MaxHeap(int maxNodes) : base(maxNodes) { + objs = new T[maxNodes + 1]; + } + + public T HeadHeapObject { get { return objs[1]; } } + + T tempObjs; + protected override void Swap(int A, int B) { + + tempHeap = heap[A]; + tempObjs = objs[A]; + + heap[A] = heap[B]; + objs[A] = objs[B]; + + heap[B] = tempHeap; + objs[B] = tempObjs; + } + + public override void PushValue(float h) { + throw new System.ArgumentException("Use PushObj(T, float)!"); + } + + public override float PopValue() { + throw new System.ArgumentException("Use Push(T, float)!"); + } + + public void PushObj(T obj, float h) { + + // if heap array is full + if(nodesCount == maxSize) { + UpsizeHeap(); + } + + nodesCount++; + heap[nodesCount] = h; + objs[nodesCount] = obj; + + BubbleUpMin(nodesCount); + } + + public T PopObj() { + + if(nodesCount == 0) + throw new System.ArgumentException("Heap is empty!"); + + T result = objs[1]; + + heap[1] = heap[nodesCount]; + objs[1] = objs[nodesCount]; + + objs[nodesCount] = default(T); + + nodesCount--; + BubbleDownMin(1); + + return result; + } + + + public T PopObj(ref float heapValue) { + + if(nodesCount == 0) + throw new System.ArgumentException("Heap is empty!"); + + heapValue = heap[1]; + T result = PopObj(); + + return result; + } + + protected override void UpsizeHeap() { + + maxSize *= 2; + System.Array.Resize(ref heap, maxSize + 1); + System.Array.Resize(ref objs, maxSize + 1); + } + + //flush internal results, returns ordered data + public void FlushResult(List resultList, List heapList = null) { + + int count = nodesCount + 1; + + if(heapList == null) { + + for(int i = 1; i < count; i++) { + resultList.Add(PopObj()); + } + } + else { + + float h = 0f; + + for(int i = 1; i < count; i++) { + resultList.Add(PopObj(ref h)); + heapList.Add(h); + } + } + } + } +} \ No newline at end of file diff --git a/Assets/Datastructures/Heap/MaxHeap.cs.meta b/Assets/Datastructures/Heap/MaxHeap.cs.meta new file mode 100644 index 0000000..fe1d25b --- /dev/null +++ b/Assets/Datastructures/Heap/MaxHeap.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6c07f133f8057584fadbaf01edc5fd3a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Datastructures/Heap/MinHeap.cs b/Assets/Datastructures/Heap/MinHeap.cs new file mode 100644 index 0000000..61d2657 --- /dev/null +++ b/Assets/Datastructures/Heap/MinHeap.cs @@ -0,0 +1,171 @@ +/*MIT License + +Copyright(c) 2018 Vili Volčini / viliwonka + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +using System.Collections.Generic; + +namespace DataStructures.ViliWonka.Heap { + + public class MinHeap : BaseHeap { + + public MinHeap(int initialSize = 2048) : base(initialSize) { + + } + + public override void PushValue(float h) { + + // if heap array is full + if(nodesCount == maxSize) { + + UpsizeHeap(); + } + + nodesCount++; + heap[nodesCount] = h; + BubbleUpMin(nodesCount); + } + + public override float PopValue() { + + if(nodesCount == 0) + throw new System.ArgumentException("Heap is empty!"); + + float result = heap[1]; + + heap[1] = heap[nodesCount]; + + nodesCount--; + + if(nodesCount != 0) + BubbleDownMin(1); + + return result; + } + } + + // generic version + public class MinHeap : MinHeap { + + T[] objs; // objects + + public MinHeap(int maxNodes = 2048) : base(maxNodes) { + objs = new T[maxNodes + 1]; + } + + public T HeadHeapObject { get { return objs[1]; } } + + T tempObjs; + protected override void Swap(int A, int B) { + + tempHeap = heap[A]; + tempObjs = objs[A]; + + heap[A] = heap[B]; + objs[A] = objs[B]; + + heap[B] = tempHeap; + objs[B] = tempObjs; + } + + public override void PushValue(float h) { + throw new System.ArgumentException("Use Push(T, float)!"); + } + + public override float PopValue() { + throw new System.ArgumentException("Use Push(T, float)!"); + } + + public void PushObj(T obj, float h) { + + // if heap array is full + if(nodesCount == maxSize) { + UpsizeHeap(); + } + + nodesCount++; + heap[nodesCount] = h; + objs[nodesCount] = obj; + + BubbleUpMin(nodesCount); + } + + public T PopObj() { + + if(nodesCount == 0) + throw new System.ArgumentException("Heap is empty!"); + + T result = objs[1]; + + heap[1] = heap[nodesCount]; + objs[1] = objs[nodesCount]; + + objs[nodesCount] = default(T); + + nodesCount--; + + if(nodesCount != 0) + BubbleDownMin(1); + + return result; + } + + public T PopObj(ref float heapValue) { + + if(nodesCount == 0) + throw new System.ArgumentException("Heap is empty!"); + + heapValue = heap[1]; + T result = PopObj(); + + return result; + } + + protected override void UpsizeHeap() { + + maxSize *= 2; + System.Array.Resize(ref heap, maxSize + 1); + System.Array.Resize(ref objs, maxSize + 1); + } + + //flush internal array, returns ordered data + public void FlushResult(List resultList, List heapList = null) { + + int count = nodesCount + 1; + + if(heapList == null) { + + for(int i = 1; i < count; i++) { + resultList.Add(PopObj()); + } + } + else { + + float h = 0f; + + for(int i = 1; i < count; i++) { + resultList.Add(PopObj(ref h)); + heapList.Add(h); + } + } + } + } +} \ No newline at end of file diff --git a/Assets/Datastructures/Heap/MinHeap.cs.meta b/Assets/Datastructures/Heap/MinHeap.cs.meta new file mode 100644 index 0000000..46a7947 --- /dev/null +++ b/Assets/Datastructures/Heap/MinHeap.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 354799470fae8d74d8f977dd8db040cf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Datastructures/Heap/heapTestScene.unity b/Assets/Datastructures/Heap/heapTestScene.unity new file mode 100644 index 0000000..3138904 --- /dev/null +++ b/Assets/Datastructures/Heap/heapTestScene.unity @@ -0,0 +1,298 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_GIWorkflowMode: 0 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_TemporalCoherenceThreshold: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 1 + m_LightmapEditorSettings: + serializedVersion: 10 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 500 + m_PVRBounces: 2 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVRFilteringMode: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ShowResolutionOverlay: 1 + m_LightingDataAsset: {fileID: 0} + m_UseShadowmask: 1 +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &653832527 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 653832530} + - component: {fileID: 653832529} + - component: {fileID: 653832528} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &653832528 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 653832527} + m_Enabled: 1 +--- !u!20 &653832529 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 653832527} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &653832530 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 653832527} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1730864583 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1730864585} + - component: {fileID: 1730864584} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &1730864584 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1730864583} + m_Enabled: 1 + serializedVersion: 8 + m_Type: 1 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_Lightmapping: 4 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &1730864585 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1730864583} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!1 &1883466098 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1883466100} + - component: {fileID: 1883466099} + m_Layer: 0 + m_Name: GameObject + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1883466099 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1883466098} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: e3f22e25c6d555d45b86d62ecaaf8895, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!4 &1883466100 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1883466098} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 1.6106011, y: 0.41596547, z: 1.608385} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} diff --git a/Assets/Datastructures/Heap/heapTestScene.unity.meta b/Assets/Datastructures/Heap/heapTestScene.unity.meta new file mode 100644 index 0000000..133bd03 --- /dev/null +++ b/Assets/Datastructures/Heap/heapTestScene.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 3bd0cd1e56a2ce749a627974769aee70 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Datastructures/KDTree.meta b/Assets/Datastructures/KDTree.meta new file mode 100644 index 0000000..6ed3456 --- /dev/null +++ b/Assets/Datastructures/KDTree.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1d2e5a2e752b9204f8375596c35adb7c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Datastructures/KDTree/KDBounds.cs b/Assets/Datastructures/KDTree/KDBounds.cs new file mode 100644 index 0000000..ce2c8db --- /dev/null +++ b/Assets/Datastructures/KDTree/KDBounds.cs @@ -0,0 +1,73 @@ +/*MIT License + +Copyright(c) 2018 Vili Volčini / viliwonka + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +using UnityEngine; +using UnityEditor; + +namespace DataStructures.ViliWonka.KDTree { + + public struct KDBounds { + + public Vector3 min; + public Vector3 max; + + public Vector3 size { + + get { + return max - min; + } + } + + // returns unity bounds + public Bounds Bounds { + + get { + return new Bounds( + (min + max) / 2, + (max - min) + ); + } + } + + + public Vector3 ClosestPoint(Vector3 point) { + + // X axis + if(point.x < min.x) point.x = min.x; + else + if(point.x > max.x) point.x = max.x; + + // Y axis + if(point.y < min.y) point.y = min.y; + else + if(point.y > max.y) point.y = max.y; + + // Z axis + if(point.z < min.z) point.z = min.z; + else + if(point.z > max.z) point.z = max.z; + + return point; + } + } +} \ No newline at end of file diff --git a/Assets/Datastructures/KDTree/KDBounds.cs.meta b/Assets/Datastructures/KDTree/KDBounds.cs.meta new file mode 100644 index 0000000..64479f3 --- /dev/null +++ b/Assets/Datastructures/KDTree/KDBounds.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 1b92db3ff3e67ee43a15eda23aea87a3 +timeCreated: 1521907406 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Datastructures/KDTree/KDNode.cs b/Assets/Datastructures/KDTree/KDNode.cs new file mode 100644 index 0000000..75b3fe7 --- /dev/null +++ b/Assets/Datastructures/KDTree/KDNode.cs @@ -0,0 +1,49 @@ +/*MIT License + +Copyright(c) 2018 Vili Volčini / viliwonka + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace DataStructures.ViliWonka.KDTree { + + public class KDNode { + + public float partitionCoordinate; + public int partitionAxis = -1; + + public KDNode negativeChild; + public KDNode positiveChild; + + public int start; + public int end; + + public int Count { get { return end - start; } } + + public bool Leaf { get { return partitionAxis == -1; } } + + public KDBounds bounds; + + }; + +} diff --git a/Assets/Datastructures/KDTree/KDNode.cs.meta b/Assets/Datastructures/KDTree/KDNode.cs.meta new file mode 100644 index 0000000..bba3b71 --- /dev/null +++ b/Assets/Datastructures/KDTree/KDNode.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: c4440ff733ae6bc488c26393873a5950 +timeCreated: 1520605573 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Datastructures/KDTree/KDQuery.meta b/Assets/Datastructures/KDTree/KDQuery.meta new file mode 100644 index 0000000..537f948 --- /dev/null +++ b/Assets/Datastructures/KDTree/KDQuery.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e3161368cf6450547b9d21f82757ed69 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Datastructures/KDTree/KDQuery/Base.cs b/Assets/Datastructures/KDTree/KDQuery/Base.cs new file mode 100644 index 0000000..1467ac0 --- /dev/null +++ b/Assets/Datastructures/KDTree/KDQuery/Base.cs @@ -0,0 +1,136 @@ +/*MIT License + +Copyright(c) 2018 Vili Volčini / viliwonka + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/* +The object used for querying. This object should be persistent - re-used for querying. +Contains internal array for pooling, so that it doesn't generate (too much) garbage. +The array never down-sizes, only up-sizes, so the more you use this object, less garbage will it make over time. + +Should be used only by 1 thread, +which means each thread should have it's own KDQuery object in order for querying to be thread safe. + +KDQuery can query different KDTrees. +*/ + + +using System.Collections.Generic; +using UnityEngine; +using System; + +namespace DataStructures.ViliWonka.KDTree { + + public partial class KDQuery { + + protected KDQueryNode[] queueArray; // queue array + protected Heap.MinHeap minHeap; //heap for k-nearest + protected int count = 0; // size of queue + protected int queryIndex = 0; // current index at stack + + /// + /// Returns initialized node from stack that also acts as a pool + /// The returned reference to node stays in stack + /// + /// Reference to pooled node + private KDQueryNode PushGetQueue() { + + KDQueryNode node = null; + + if (count < queueArray.Length) { + + if (queueArray[count] == null) + queueArray[count] = node = new KDQueryNode(); + else + node = queueArray[count]; + } + else { + + // automatic resize of pool + Array.Resize(ref queueArray, queueArray.Length * 2); + node = queueArray[count] = new KDQueryNode(); + } + + count++; + + return node; + } + + protected void PushToQueue(KDNode node, Vector3 tempClosestPoint) { + + var queryNode = PushGetQueue(); + queryNode.node = node; + queryNode.tempClosestPoint = tempClosestPoint; + } + + protected void PushToHeap(KDNode node, Vector3 tempClosestPoint, Vector3 queryPosition) { + + var queryNode = PushGetQueue(); + queryNode.node = node; + queryNode.tempClosestPoint = tempClosestPoint; + + float sqrDist = Vector3.SqrMagnitude(tempClosestPoint - queryPosition); + queryNode.distance = sqrDist; + minHeap.PushObj(queryNode, sqrDist); + } + + protected int LeftToProcess { + + get { + return count - queryIndex; + } + } + + // just gets unprocessed node from stack + // increases queryIndex + protected KDQueryNode PopFromQueue() { + + var node = queueArray[queryIndex]; + queryIndex++; + + return node; + } + + protected KDQueryNode PopFromHeap() { + + KDQueryNode heapNode = minHeap.PopObj(); + + queueArray[queryIndex]= heapNode; + queryIndex++; + + return heapNode; + } + + protected void Reset() { + + count = 0; + queryIndex = 0; + minHeap.Clear(); + } + + public KDQuery(int queryNodesContainersInitialSize = 2048) { + queueArray = new KDQueryNode[queryNodesContainersInitialSize]; + minHeap = new Heap.MinHeap(queryNodesContainersInitialSize); + } + + } + +} \ No newline at end of file diff --git a/Assets/Datastructures/KDTree/KDQuery/Base.cs.meta b/Assets/Datastructures/KDTree/KDQuery/Base.cs.meta new file mode 100644 index 0000000..58d315b --- /dev/null +++ b/Assets/Datastructures/KDTree/KDQuery/Base.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 57b0c8c3d7b727847b235e82d2fbead0 +timeCreated: 1521924674 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Datastructures/KDTree/KDQuery/Debug.cs b/Assets/Datastructures/KDTree/KDQuery/Debug.cs new file mode 100644 index 0000000..3664f1f --- /dev/null +++ b/Assets/Datastructures/KDTree/KDQuery/Debug.cs @@ -0,0 +1,56 @@ +/*MIT License + +Copyright(c) 2018 Vili Volčini / viliwonka + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#define KDTREE_VISUAL_DEBUG + +using System.Collections.Generic; +using UnityEngine; +using System; + +namespace DataStructures.ViliWonka.KDTree { + + public partial class KDQuery { + + // uses gizmos + public void DrawLastQuery() { + + Color start = Color.red; + Color end = Color.green; + + start.a = 0.25f; + end.a = 0.25f; + + for(int i = 0; i < queryIndex; i++) { + + float val = i / (float)queryIndex; + + Gizmos.color = Color.Lerp(end, start, val); + + Bounds b = queueArray[i].node.bounds.Bounds; + + Gizmos.DrawWireCube(b.center, b.size); + } + } + } + +} \ No newline at end of file diff --git a/Assets/Datastructures/KDTree/KDQuery/Debug.cs.meta b/Assets/Datastructures/KDTree/KDQuery/Debug.cs.meta new file mode 100644 index 0000000..d252614 --- /dev/null +++ b/Assets/Datastructures/KDTree/KDQuery/Debug.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4aac471bf6e30fd49afa5fff399421c0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Datastructures/KDTree/KDQuery/KDQueryNode.cs b/Assets/Datastructures/KDTree/KDQuery/KDQueryNode.cs new file mode 100644 index 0000000..c0a5bc2 --- /dev/null +++ b/Assets/Datastructures/KDTree/KDQuery/KDQueryNode.cs @@ -0,0 +1,45 @@ +/*MIT License + +Copyright(c) 2018 Vili Volčini / viliwonka + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +using UnityEngine; +using UnityEditor; + +namespace DataStructures.ViliWonka.KDTree { + + public class KDQueryNode { + + public KDNode node; + public Vector3 tempClosestPoint; + public float distance; + + public KDQueryNode() { + + } + + public KDQueryNode(KDNode node, Vector3 tempClosestPoint) { + this.node = node; + this.tempClosestPoint = tempClosestPoint; + } + + } +} \ No newline at end of file diff --git a/Assets/Datastructures/KDTree/KDQuery/KDQueryNode.cs.meta b/Assets/Datastructures/KDTree/KDQuery/KDQueryNode.cs.meta new file mode 100644 index 0000000..f6e9450 --- /dev/null +++ b/Assets/Datastructures/KDTree/KDQuery/KDQueryNode.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: cbe8b45a46d50254494605e93da9d3ed +timeCreated: 1521907406 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Datastructures/KDTree/KDQuery/QueryClosest.cs b/Assets/Datastructures/KDTree/KDQuery/QueryClosest.cs new file mode 100644 index 0000000..20586ae --- /dev/null +++ b/Assets/Datastructures/KDTree/KDQuery/QueryClosest.cs @@ -0,0 +1,144 @@ +/*MIT License + +Copyright(c) 2018 Vili Volčini / viliwonka + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +using System.Collections.Generic; +using UnityEngine; +using System; + +namespace DataStructures.ViliWonka.KDTree { + + using Heap; + + public partial class KDQuery { + + public void ClosestPoint(KDTree tree, Vector3 queryPosition, List resultIndices, List resultDistances = null) { + + Reset(); + + Vector3[] points = tree.Points; + int[] permutation = tree.Permutation; + + if (points.Length == 0) { + return; + } + + int smallestIndex = 0; + /// Smallest Squared Radius + float SSR = Single.PositiveInfinity; + + + var rootNode = tree.RootNode; + + Vector3 rootClosestPoint = rootNode.bounds.ClosestPoint(queryPosition); + + PushToHeap(rootNode, rootClosestPoint, queryPosition); + + KDQueryNode queryNode = null; + KDNode node = null; + + int partitionAxis; + float partitionCoord; + + Vector3 tempClosestPoint; + + // searching + while(minHeap.Count > 0) { + + queryNode = PopFromHeap(); + + if(queryNode.distance > SSR) + continue; + + node = queryNode.node; + + if(!node.Leaf) { + + partitionAxis = node.partitionAxis; + partitionCoord = node.partitionCoordinate; + + tempClosestPoint = queryNode.tempClosestPoint; + + if((tempClosestPoint[partitionAxis] - partitionCoord) < 0) { + + // we already know we are on the side of negative bound/node, + // so we don't need to test for distance + // push to stack for later querying + + PushToHeap(node.negativeChild, tempClosestPoint, queryPosition); + // project the tempClosestPoint to other bound + tempClosestPoint[partitionAxis] = partitionCoord; + + if(node.positiveChild.Count != 0) { + + PushToHeap(node.positiveChild, tempClosestPoint, queryPosition); + } + + } + else { + + // we already know we are on the side of positive bound/node, + // so we don't need to test for distance + // push to stack for later querying + + PushToHeap(node.positiveChild, tempClosestPoint, queryPosition); + // project the tempClosestPoint to other bound + tempClosestPoint[partitionAxis] = partitionCoord; + + if(node.positiveChild.Count != 0) { + + PushToHeap(node.negativeChild, tempClosestPoint, queryPosition); + } + + } + } + else { + + float sqrDist; + // LEAF + for(int i = node.start; i < node.end; i++) { + + int index = permutation[i]; + + sqrDist = Vector3.SqrMagnitude(points[index] - queryPosition); + + if(sqrDist <= SSR) { + + SSR = sqrDist; + smallestIndex = index; + } + } + + } + } + + resultIndices.Add(smallestIndex); + + if(resultDistances != null) { + resultDistances.Add(SSR); + } + + } + + } + +} diff --git a/Assets/Datastructures/KDTree/KDQuery/QueryClosest.cs.meta b/Assets/Datastructures/KDTree/KDQuery/QueryClosest.cs.meta new file mode 100644 index 0000000..fe53a59 --- /dev/null +++ b/Assets/Datastructures/KDTree/KDQuery/QueryClosest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a226a270de4df034986ad64328da4cc2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Datastructures/KDTree/KDQuery/QueryInterval.cs b/Assets/Datastructures/KDTree/KDQuery/QueryInterval.cs new file mode 100644 index 0000000..88d1f1d --- /dev/null +++ b/Assets/Datastructures/KDTree/KDQuery/QueryInterval.cs @@ -0,0 +1,151 @@ +/*MIT License + +Copyright(c) 2018 Vili Volčini / viliwonka + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +using System.Collections.Generic; +using UnityEngine; +using System; + +namespace DataStructures.ViliWonka.KDTree { + + public partial class KDQuery { + + public void Interval(KDTree tree, Vector3 min, Vector3 max, List resultIndices) { + + Reset(); + + Vector3[] points = tree.Points; + int[] permutation = tree.Permutation; + + var rootNode = tree.RootNode; + + PushToQueue( + + rootNode, + rootNode.bounds.ClosestPoint((min + max) / 2) + ); + + KDQueryNode queryNode = null; + KDNode node = null; + + + // KD search with pruning (don't visit areas which distance is more away than range) + // Recursion done on Stack + while(LeftToProcess > 0) { + + queryNode = PopFromQueue(); + node = queryNode.node; + + if(!node.Leaf) { + + int partitionAxis = node.partitionAxis; + float partitionCoord = node.partitionCoordinate; + + Vector3 tempClosestPoint = queryNode.tempClosestPoint; + + if((tempClosestPoint[partitionAxis] - partitionCoord) < 0) { + + // we already know we are inside negative bound/node, + // so we don't need to test for distance + // push to stack for later querying + + // tempClosestPoint is inside negative side + // assign it to negativeChild + PushToQueue(node.negativeChild, tempClosestPoint); + + tempClosestPoint[partitionAxis] = partitionCoord; + + // testing other side + if(node.positiveChild.Count != 0 + && tempClosestPoint[partitionAxis] <= max[partitionAxis]) { + + PushToQueue(node.positiveChild, tempClosestPoint); + } + } + else { + + // we already know we are inside positive bound/node, + // so we don't need to test for distance + // push to stack for later querying + + // tempClosestPoint is inside positive side + // assign it to positiveChild + PushToQueue(node.positiveChild, tempClosestPoint); + + // project the tempClosestPoint to other bound + tempClosestPoint[partitionAxis] = partitionCoord; + + // testing other side + if(node.negativeChild.Count != 0 + && tempClosestPoint[partitionAxis] >= min[partitionAxis]) { + + PushToQueue(node.negativeChild, tempClosestPoint); + } + } + } + else { + + // LEAF + + // testing if node bounds are inside the query interval + if(node.bounds.min[0] >= min[0] + && node.bounds.min[1] >= min[1] + && node.bounds.min[2] >= min[2] + + && node.bounds.max[0] <= max[0] + && node.bounds.max[1] <= max[1] + && node.bounds.max[2] <= max[2]) { + + for(int i = node.start; i < node.end; i++) { + + resultIndices.Add(permutation[i]); + } + + } + // node is not inside query interval, need to do test on each point separately + else { + + for(int i = node.start; i < node.end; i++) { + + int index = permutation[i]; + + Vector3 v = points[index]; + + if(v[0] >= min[0] + && v[1] >= min[1] + && v[2] >= min[2] + + && v[0] <= max[0] + && v[1] <= max[1] + && v[2] <= max[2]) { + + resultIndices.Add(index); + } + } + } + + } + } + } + } + +} \ No newline at end of file diff --git a/Assets/Datastructures/KDTree/KDQuery/QueryInterval.cs.meta b/Assets/Datastructures/KDTree/KDQuery/QueryInterval.cs.meta new file mode 100644 index 0000000..6cb6e22 --- /dev/null +++ b/Assets/Datastructures/KDTree/KDQuery/QueryInterval.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ea62a128d2f9e7848ae1c116012e36e1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Datastructures/KDTree/KDQuery/QueryKNearest.cs b/Assets/Datastructures/KDTree/KDQuery/QueryKNearest.cs new file mode 100644 index 0000000..d1b5044 --- /dev/null +++ b/Assets/Datastructures/KDTree/KDQuery/QueryKNearest.cs @@ -0,0 +1,160 @@ +/*MIT License + +Copyright(c) 2018 Vili Volčini / viliwonka + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#define KDTREE_VISUAL_DEBUG + +using System.Collections.Generic; +using UnityEngine; +using System; + +namespace DataStructures.ViliWonka.KDTree { + + using Heap; + + public partial class KDQuery { + + SortedList> _heaps = new SortedList>(); + /// + /// Returns indices to k closest points, and optionaly can return distances + /// + /// Tree to do search on + /// Position + /// Max number of points + /// List where resulting indices will be stored + /// Optional list where resulting distances will be stored + public void KNearest(KDTree tree, Vector3 queryPosition, int k, List resultIndices, List resultDistances = null) { + + // pooled heap arrays + KSmallestHeap kHeap; + + _heaps.TryGetValue(k, out kHeap); + + if(kHeap == null) { + + kHeap = new KSmallestHeap(k); + _heaps.Add(k, kHeap); + } + + kHeap.Clear(); + Reset(); + + Vector3[] points = tree.Points; + int[] permutation = tree.Permutation; + + ///Biggest Smallest Squared Radius + float BSSR = Single.PositiveInfinity; + + var rootNode = tree.RootNode; + + Vector3 rootClosestPoint = rootNode.bounds.ClosestPoint(queryPosition); + + PushToHeap(rootNode, rootClosestPoint, queryPosition); + + KDQueryNode queryNode = null; + KDNode node = null; + + int partitionAxis; + float partitionCoord; + + Vector3 tempClosestPoint; + + // searching + while(minHeap.Count > 0) { + + queryNode = PopFromHeap(); + + if(queryNode.distance > BSSR) + continue; + + node = queryNode.node; + + if(!node.Leaf) { + + partitionAxis = node.partitionAxis; + partitionCoord = node.partitionCoordinate; + + tempClosestPoint = queryNode.tempClosestPoint; + + if((tempClosestPoint[partitionAxis] - partitionCoord) < 0) { + + // we already know we are on the side of negative bound/node, + // so we don't need to test for distance + // push to stack for later querying + + PushToHeap(node.negativeChild, tempClosestPoint, queryPosition); + // project the tempClosestPoint to other bound + tempClosestPoint[partitionAxis] = partitionCoord; + + if(node.positiveChild.Count != 0) { + + PushToHeap(node.positiveChild, tempClosestPoint, queryPosition); + } + + } + else { + + // we already know we are on the side of positive bound/node, + // so we don't need to test for distance + // push to stack for later querying + + PushToHeap(node.positiveChild, tempClosestPoint, queryPosition); + // project the tempClosestPoint to other bound + tempClosestPoint[partitionAxis] = partitionCoord; + + if(node.positiveChild.Count != 0) { + + PushToHeap(node.negativeChild, tempClosestPoint, queryPosition); + } + + } + } + else { + + float sqrDist; + // LEAF + for(int i = node.start; i < node.end; i++) { + + int index = permutation[i]; + + sqrDist = Vector3.SqrMagnitude(points[index] - queryPosition); + + if(sqrDist <= BSSR) { + + kHeap.PushObj(index, sqrDist); + + if(kHeap.Full) { + BSSR = kHeap.HeadValue; + } + } + } + + } + } + + kHeap.FlushResult(resultIndices, resultDistances); + + } + + } + +} \ No newline at end of file diff --git a/Assets/Datastructures/KDTree/KDQuery/QueryKNearest.cs.meta b/Assets/Datastructures/KDTree/KDQuery/QueryKNearest.cs.meta new file mode 100644 index 0000000..3d2ff59 --- /dev/null +++ b/Assets/Datastructures/KDTree/KDQuery/QueryKNearest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ded2e8ab341672849898f46a7719a7a0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Datastructures/KDTree/KDQuery/QueryRadius.cs b/Assets/Datastructures/KDTree/KDQuery/QueryRadius.cs new file mode 100644 index 0000000..41c6720 --- /dev/null +++ b/Assets/Datastructures/KDTree/KDQuery/QueryRadius.cs @@ -0,0 +1,131 @@ +/*MIT License + +Copyright(c) 2018 Vili Volčini / viliwonka + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +using System.Collections.Generic; +using UnityEngine; +using System; + +namespace DataStructures.ViliWonka.KDTree { + + public partial class KDQuery { + + /// + /// Search by radius method. + /// + /// Tree to do search on + /// Position + /// Radius + /// Initialized list, cleared. + public void Radius(KDTree tree, Vector3 queryPosition, float queryRadius, List resultIndices) { + + Reset(); + + Vector3[] points = tree.Points; + int[] permutation = tree.Permutation; + + float squaredRadius = queryRadius * queryRadius; + + var rootNode = tree.RootNode; + + PushToQueue(rootNode, rootNode.bounds.ClosestPoint(queryPosition)); + + KDQueryNode queryNode = null; + KDNode node = null; + + // KD search with pruning (don't visit areas which distance is more away than range) + // Recursion done on Stack + while(LeftToProcess > 0) { + + queryNode = PopFromQueue(); + node = queryNode.node; + + if(!node.Leaf) { + + int partitionAxis = node.partitionAxis; + float partitionCoord = node.partitionCoordinate; + + Vector3 tempClosestPoint = queryNode.tempClosestPoint; + + if((tempClosestPoint[partitionAxis] - partitionCoord) < 0) { + + // we already know we are inside negative bound/node, + // so we don't need to test for distance + // push to stack for later querying + + // tempClosestPoint is inside negative side + // assign it to negativeChild + PushToQueue(node.negativeChild, tempClosestPoint); + + tempClosestPoint[partitionAxis] = partitionCoord; + + float sqrDist = Vector3.SqrMagnitude(tempClosestPoint - queryPosition); + + // testing other side + if(node.positiveChild.Count != 0 + && sqrDist <= squaredRadius) { + + PushToQueue(node.positiveChild, tempClosestPoint); + } + } + else { + + // we already know we are inside positive bound/node, + // so we don't need to test for distance + // push to stack for later querying + + // tempClosestPoint is inside positive side + // assign it to positiveChild + PushToQueue(node.positiveChild, tempClosestPoint); + + // project the tempClosestPoint to other bound + tempClosestPoint[partitionAxis] = partitionCoord; + + float sqrDist = Vector3.SqrMagnitude(tempClosestPoint - queryPosition); + + // testing other side + if(node.negativeChild.Count != 0 + && sqrDist <= squaredRadius) { + + PushToQueue(node.negativeChild, tempClosestPoint); + } + } + } + else { + + // LEAF + for(int i = node.start; i < node.end; i++) { + + int index = permutation[i]; + + if(Vector3.SqrMagnitude(points[index] - queryPosition) <= squaredRadius) { + + resultIndices.Add(index); + } + } + + } + } + } + + } +} \ No newline at end of file diff --git a/Assets/Datastructures/KDTree/KDQuery/QueryRadius.cs.meta b/Assets/Datastructures/KDTree/KDQuery/QueryRadius.cs.meta new file mode 100644 index 0000000..2a0d895 --- /dev/null +++ b/Assets/Datastructures/KDTree/KDQuery/QueryRadius.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 17086d61d13921a4ba22e206ef32e74f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Datastructures/KDTree/KDTree.cs b/Assets/Datastructures/KDTree/KDTree.cs new file mode 100644 index 0000000..9d27635 --- /dev/null +++ b/Assets/Datastructures/KDTree/KDTree.cs @@ -0,0 +1,459 @@ +/*MIT License + +Copyright(c) 2018 Vili Volčini / viliwonka + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// change to !KDTREE_DUPLICATES +// if you know for sure you will not use duplicate coordinates (all unique) +#define KDTREE_DUPLICATES + +using System.Collections; +using System.Collections.Generic; +using System; +using UnityEngine; + +namespace DataStructures.ViliWonka.KDTree { + + public class KDTree { + + public KDNode RootNode { get; private set; } + + public Vector3[] Points { get { return points; } } // points on which kd-tree will build on. This array will stay unchanged when re/building kdtree! + private Vector3[] points; + + public int[] Permutation { get { return permutation; } } // index aray, that will be permuted + private int[] permutation; + + public int Count { get; private set; } + + private int maxPointsPerLeafNode = 32; + + private KDNode[] kdNodesStack; + private int kdNodesCount = 0; + + public KDTree(int maxPointsPerLeafNode = 32) { + + Count = 0; + points = new Vector3[0]; + permutation = new int[0]; + + kdNodesStack = new KDNode[64]; + + this.maxPointsPerLeafNode = maxPointsPerLeafNode; + } + + public KDTree(Vector3[] points, int maxPointsPerLeafNode = 32) { + + this.points = points; + this.permutation = new int[points.Length]; + + Count = points.Length; + kdNodesStack = new KDNode[64]; + + this.maxPointsPerLeafNode = maxPointsPerLeafNode; + + Rebuild(); + } + + public void Build(Vector3[] newPoints, int maxPointsPerLeafNode = -1) { + + SetCount(newPoints.Length); + + for(int i = 0; i < Count; i++) { + points[i] = newPoints[i]; + } + + Rebuild(maxPointsPerLeafNode); + } + + public void Build(List newPoints, int maxPointsPerLeafNode = -1) { + + SetCount(newPoints.Count); + + for(int i = 0; i < Count; i++) { + points[i] = newPoints[i]; + } + + Rebuild(maxPointsPerLeafNode); + } + + public void Rebuild(int maxPointsPerLeafNode = -1) { + + for(int i = 0; i < Count; i++) { + permutation[i] = i; + } + + if(maxPointsPerLeafNode > 0) { + this.maxPointsPerLeafNode = maxPointsPerLeafNode; + } + + BuildTree(); + } + + public void SetCount(int newSize) { + + Count = newSize; + // upsize internal arrays + if(Count > points.Length) { + + Array.Resize(ref points, Count); + Array.Resize(ref permutation, Count); + } + } + + void BuildTree() { + + ResetKDNodeStack(); + + RootNode = GetKDNode(); + RootNode.bounds = MakeBounds(); + RootNode.start = 0; + RootNode.end = Count; + + SplitNode(RootNode); + } + + KDNode GetKDNode() { + + KDNode node = null; + + if(kdNodesCount < kdNodesStack.Length) { + + if(kdNodesStack[kdNodesCount] == null) { + kdNodesStack[kdNodesCount] = node = new KDNode(); + } + else { + node = kdNodesStack[kdNodesCount]; + node.partitionAxis = -1; + } + } + else { + + // automatic resize of KDNode pool array + Array.Resize(ref kdNodesStack, kdNodesStack.Length * 2); + node = kdNodesStack[kdNodesCount] = new KDNode(); + } + + kdNodesCount++; + + return node; + } + + void ResetKDNodeStack() { + kdNodesCount = 0; + } + + /// + /// For calculating root node bounds + /// + /// Boundary of all Vector3 points + KDBounds MakeBounds() { + + Vector3 max = new Vector3(float.MinValue, float.MinValue, float.MinValue); + Vector3 min = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue); + + int even = Count & ~1; // calculate even Length + + // min, max calculations + // 3n/2 calculations instead of 2n + for (int i0 = 0; i0 < even; i0 += 2) { + + int i1 = i0 + 1; + + // X Coords + if (points[i0].x > points[i1].x) { + // i0 is bigger, i1 is smaller + if (points[i1].x < min.x) + min.x = points[i1].x; + + if (points[i0].x > max.x) + max.x = points[i0].x; + } + else { + // i1 is smaller, i0 is bigger + if (points[i0].x < min.x) + min.x = points[i0].x; + + if (points[i1].x > max.x) + max.x = points[i1].x; + } + + // Y Coords + if (points[i0].y > points[i1].y) { + // i0 is bigger, i1 is smaller + if (points[i1].y < min.y) + min.y = points[i1].y; + + if (points[i0].y > max.y) + max.y = points[i0].y; + } + else { + // i1 is smaller, i0 is bigger + if (points[i0].y < min.y) + min.y = points[i0].y; + + if (points[i1].y > max.y) + max.y = points[i1].y; + } + + // Z Coords + if (points[i0].z > points[i1].z) { + // i0 is bigger, i1 is smaller + if (points[i1].z < min.z) + min.z = points[i1].z; + + if (points[i0].z > max.z) + max.z = points[i0].z; + } + else { + // i1 is smaller, i0 is bigger + if (points[i0].z < min.z) + min.z = points[i0].z; + + if (points[i1].z > max.z) + max.z = points[i1].z; + } + } + + // if array was odd, calculate also min/max for the last element + if(even != Count) { + // X + if (min.x > points[even].x) + min.x = points[even].x; + + if (max.x < points[even].x) + max.x = points[even].x; + // Y + if (min.y > points[even].y) + min.y = points[even].y; + + if (max.y < points[even].y) + max.y = points[even].y; + // Z + if (min.z > points[even].z) + min.z = points[even].z; + + if (max.z < points[even].z) + max.z = points[even].z; + } + + KDBounds b = new KDBounds(); + b.min = min; + b.max = max; + + return b; + } + + /// + /// Recursive splitting procedure + /// + /// This is where root node goes + /// + /// + void SplitNode(KDNode parent) { + + // center of bounding box + KDBounds parentBounds = parent.bounds; + Vector3 parentBoundsSize = parentBounds.size; + + // Find axis where bounds are largest + int splitAxis = 0; + float axisSize = parentBoundsSize.x; + + if (axisSize < parentBoundsSize.y) { + splitAxis = 1; + axisSize = parentBoundsSize.y; + } + + if (axisSize < parentBoundsSize.z) { + splitAxis = 2; + } + + // Our axis min-max bounds + float boundsStart = parentBounds.min[splitAxis]; + float boundsEnd = parentBounds.max[splitAxis]; + + // Calculate the spliting coords + float splitPivot = CalculatePivot(parent.start, parent.end, boundsStart, boundsEnd, splitAxis); + + parent.partitionAxis = splitAxis; + parent.partitionCoordinate = splitPivot; + + // 'Spliting' array to two subarrays + int splittingIndex = Partition(parent.start, parent.end, splitPivot, splitAxis); + + // Negative / Left node + Vector3 negMax = parentBounds.max; + negMax[splitAxis] = splitPivot; + + KDNode negNode = GetKDNode(); + negNode.bounds = parentBounds; + negNode.bounds.max = negMax; + negNode.start = parent.start; + negNode.end = splittingIndex; + parent.negativeChild = negNode; + + // Positive / Right node + Vector3 posMin = parentBounds.min; + posMin[splitAxis] = splitPivot; + + KDNode posNode = GetKDNode(); + posNode.bounds = parentBounds; + posNode.bounds.min = posMin; + posNode.start = splittingIndex; + posNode.end = parent.end; + parent.positiveChild = posNode; + + // check if we are actually splitting it anything + // this if check enables duplicate coordinates, but makes construction a bit slower +#if KDTREE_DUPLICATES + if(negNode.Count != 0 && posNode.Count != 0) { + #endif + // Constraint function deciding if split should be continued + if(ContinueSplit(negNode)) + SplitNode(negNode); + + + if(ContinueSplit(posNode)) + SplitNode(posNode); + +#if KDTREE_DUPLICATES + } +#endif + } + + /// + /// Sliding midpoint splitting pivot calculation + /// 1. First splits node to two equal parts (midPoint) + /// 2. Checks if elements are in both sides of splitted bounds + /// 3a. If they are, just return midPoint + /// 3b. If they are not, then points are only on left or right bound. + /// 4. Move the splitting pivot so that it shrinks part with points completely (calculate min or max dependent) and return. + /// + /// + /// + /// + /// + /// + /// + float CalculatePivot(int start, int end, float boundsStart, float boundsEnd, int axis) { + + //! sliding midpoint rule + float midPoint = (boundsStart + boundsEnd) / 2f; + + bool negative = false; + bool positive = false; + + float negMax = Single.MinValue; + float posMin = Single.MaxValue; + + // this for loop section is used both for sorted and unsorted data + for (int i = start; i < end; i++) { + + if (points[permutation[i]][axis] < midPoint) + negative = true; + else + positive = true; + + if (negative == true && positive == true) + return midPoint; + } + + if (negative) { + + for (int i = start; i < end; i++) + if (negMax < points[permutation[i]][axis]) + negMax = points[permutation[i]][axis]; + + return negMax; + } + else { + + for (int i = start; i < end; i++) + if (posMin > points[permutation[i]][axis]) + posMin = points[permutation[i]][axis]; + + return posMin; + } + } + + /// + /// Similar to Hoare partitioning algorithm (used in Quick Sort) + /// Modification: pivot is not left-most element but is instead argument of function + /// Calculates splitting index and partially sorts elements (swaps them until they are on correct side - depending on pivot) + /// Complexity: O(n) + /// + /// Start index + /// End index + /// Pivot that decides boundary between left and right + /// Axis of this pivoting + /// + /// Returns splitting index that subdivides array into 2 smaller arrays + /// left = [start, pivot), + /// right = [pivot, end) + /// + int Partition(int start, int end, float partitionPivot, int axis) { + + // note: increasing right pointer is actually decreasing! + int LP = start - 1; // left pointer (negative side) + int RP = end; // right pointer (positive side) + + int temp; // temporary var for swapping permutation indexes + + while (true) { + + do { + // move from left to the right until "out of bounds" value is found + LP++; + } + while (LP < RP && points[permutation[LP]][axis] < partitionPivot); + + do { + // move from right to the left until "out of bounds" value found + RP--; + } + while (LP < RP && points[permutation[RP]][axis] >= partitionPivot); + + if (LP < RP) { + // swap + temp = permutation[LP]; + permutation[LP] = permutation[RP]; + permutation[RP] = temp; + } + else { + + return LP; + } + } + } + + /// + /// Constraint function. You can add custom constraints here - if you have some other data/classes binded to Vector3 points + /// Can hardcode it into + /// + /// + /// + bool ContinueSplit(KDNode node) { + + return (node.Count > maxPointsPerLeafNode); + } + } +} \ No newline at end of file diff --git a/Assets/Datastructures/KDTree/KDTree.cs.meta b/Assets/Datastructures/KDTree/KDTree.cs.meta new file mode 100644 index 0000000..0cb1eed --- /dev/null +++ b/Assets/Datastructures/KDTree/KDTree.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 437ce4903d14e2f4182fad0f2ac92678 +timeCreated: 1520696434 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Planet.cs b/Assets/Planet.cs index b451aee..c101f0c 100644 --- a/Assets/Planet.cs +++ b/Assets/Planet.cs @@ -1,6 +1,9 @@ using System.Collections.Generic; using System.Linq; using UnityEngine; +using DataStructures.ViliWonka.KDTree; +using System; +using System.Text; public class Planet : MonoBehaviour { @@ -8,7 +11,10 @@ public class Planet : MonoBehaviour private LineRenderer _lineRenderer; private CircleCollider2D _circleCollider; private Network _network; - private HashSet _units; + private KDQuery _query; + public KDTree Tree => _tree; + private KDTree _tree; + private List _units; [SerializeField] public HashSet neighbors; @@ -22,11 +28,14 @@ public class Planet : MonoBehaviour { _lineRenderer = gameObject.GetComponent(); _network = FindObjectOfType(); - _units = new HashSet(); - } + _units = new List(); + _query = new KDQuery(); + _tree = new KDTree(); - void OnEnable() - { + while (_units.Count < 10) + SpawnUnit(); + + InvokeRepeating("ChangeUnits", 0.0f, 1f); } void OnMouseDown() @@ -41,15 +50,74 @@ public class Planet : MonoBehaviour return new Vector2(Mathf.Cos(theta), Mathf.Sin(theta)) * radius; } + public float GetUnitAngle(Unit unit) + { + var angle = Mathf.Atan2(unit.transform.position.y, unit.transform.position.x) * Mathf.Rad2Deg; + return angle < 0 ? angle + 360 : angle; + } + void SpawnUnit() { var unitPrefab = Resources.Load("BaseUnit"); var unitObject = Instantiate(unitPrefab); unitObject.transform.position = transform.position; unitObject.transform.parent = transform; - _units.Add(unitObject.GetComponent()); - Debug.Log("Spawned unit " + unitObject.GetComponent().GetHashCode()); + var unit = unitObject.GetComponent(); + _units.Add(unit); unitObject.name = "Unit " + _units.Count; + + + _tree.SetCount(_tree.Count + 1); + var index = _tree.Count - 1; + unit.TreeIndex = index; + + _tree.Points[index] = new Vector2(unitObject.transform.position.x, unitObject.transform.position.y); + _tree.Rebuild(); + + } + + void FixedUpdate() + { + } + + void ChangeUnits() + { + // Delete 3 units + for (int i = 0; i < 3; i++) + { + if (_units.Count == 0) break; + var unit = _units[0]; + Destroy(unit.gameObject); + } + + // Add 4 units + for (int i = 0; i < 4; i++) + { + SpawnUnit(); + } + + } + + void OnDrawGizmos() + { + if (_units.Count == 0) return; + + var origin = _units[0]; + // Draw sphere on first unit + Gizmos.color = Color.red; + Gizmos.DrawSphere(origin.transform.position, 0.1f); + + var results = new List(); + var resultDistances = new List(); + _query.KNearest(_tree, origin.transform.position, 2, results, resultDistances); + + if (results.Count < 2) return; + + var closestUnit = results[0]; + + // Draw line to closest unit + Gizmos.color = Color.green; + Gizmos.DrawLine(origin.transform.position, _tree.Points[closestUnit]); } bool IsConnected(Planet other) @@ -64,6 +132,7 @@ public class Planet : MonoBehaviour public void UnitDestroyed(Unit unit) { + _tree.Points[unit.TreeIndex] = new Vector2(float.MaxValue, float.MaxValue); _units.Remove(unit); } diff --git a/Assets/Resources/BaseUnit.prefab b/Assets/Resources/BaseUnit.prefab index 7a36c9f..ae15a9c 100644 --- a/Assets/Resources/BaseUnit.prefab +++ b/Assets/Resources/BaseUnit.prefab @@ -53,6 +53,7 @@ MonoBehaviour: fillColor: {r: 0.509434, g: 0.509434, b: 0.509434, a: 1} edgeColor: {r: 0.1792453, g: 0.1792453, b: 0.1792453, a: 1} Size: 16 + TreeIndex: 0 --- !u!33 &6421509436193620488 MeshFilter: m_ObjectHideFlags: 0 @@ -246,7 +247,7 @@ MonoBehaviour: m_BlendStyleIndex: 0 m_FalloffIntensity: 0.5 m_Color: {r: 1, g: 1, b: 1, a: 1} - m_Intensity: 26.48 + m_Intensity: 30 m_LightVolumeIntensity: 1 m_LightVolumeIntensityEnabled: 0 m_ApplyToSortingLayers: diff --git a/Assets/Unit.cs b/Assets/Unit.cs index 00b7fc2..26b67a4 100644 --- a/Assets/Unit.cs +++ b/Assets/Unit.cs @@ -1,5 +1,7 @@ +using System; using System.Linq; using UnityEngine; +using Random = UnityEngine.Random; public class Unit : MonoBehaviour { @@ -9,6 +11,9 @@ public class Unit : MonoBehaviour public Color fillColor = Color.white; public Color edgeColor = Color.white; public float Size; + public int TreeIndex; + private float RotationSpeed; + private float BobbingOffset; void Start() { @@ -19,15 +24,28 @@ public class Unit : MonoBehaviour { x = (Random.value > 0.5f ? 1 : -1) * 32 * Random.Range(0.8f, 1.2f), }; + RotationSpeed = Random.value > 0.5f ? 1 : -1 * Random.Range(0.8f, 1.2f) * 4f; + BobbingOffset = Random.Range(0, (float)(2 * Math.PI)); - transform.position = planet.GetSurfacePosition(Random.Range(0, 360), 0.4f); + transform.position = planet.GetSurfacePosition(Random.Range(0, 360), 0.3f); } void Update() { + // Rotate itself slightly + transform.Rotate(new Vector3(0, 0, Time.deltaTime * RotationSpeed)); transform.RotateAround(planet.transform.position, Vector3.forward, planetaryVelocity.x * Time.deltaTime); - transform.position = Vector2.MoveTowards(transform.position, planet.transform.position, planetaryVelocity.y * Time.deltaTime); + + // Bobbing motion + var unitAngle = planet.GetUnitAngle(this); + var targetDistance = (Mathf.Sin(BobbingOffset + Time.time) + 1) / 2; + targetDistance = Mathf.Lerp(0.35f, 0.8f, targetDistance); + if (TreeIndex == 0) + Debug.Log(targetDistance); + transform.position = planet.GetSurfacePosition(unitAngle, targetDistance); + + planet.Tree.Points[TreeIndex] = transform.position; } private void OnDestroy()