diff --git a/documentation/docs/05-changelog.md b/documentation/docs/05-changelog.md index c5c387864..cc999a702 100644 --- a/documentation/docs/05-changelog.md +++ b/documentation/docs/05-changelog.md @@ -1,8 +1,12 @@ ## CHANGELOG -### v.1.4.0 +### v.1.4.1 *??/??/2018* ##### +- Add two new modules, KdTreeCollection and AddToCollection Gameobject modifier. +- Add Collider option for vector features. +- Fix to make filter values case insensitive. +- Fix issue where position vector features was not being set. - Fix `Range Property` extrusion option for vector features. - Add scale factor for extrusion value derived from feature property. diff --git a/sdkproject/Assets/Mapbox/Examples/1_DataExplorer/BuildingCollection.asset b/sdkproject/Assets/Mapbox/Examples/1_DataExplorer/BuildingCollection.asset new file mode 100644 index 000000000..80c60bb91 --- /dev/null +++ b/sdkproject/Assets/Mapbox/Examples/1_DataExplorer/BuildingCollection.asset @@ -0,0 +1,14 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 82ed8ed837e25084bbe8a37d53c5b77b, type: 3} + m_Name: BuildingCollection + m_EditorClassIdentifier: + Count: 2406 diff --git a/sdkproject/Assets/Mapbox/Examples/1_DataExplorer/BuildingCollection.asset.meta b/sdkproject/Assets/Mapbox/Examples/1_DataExplorer/BuildingCollection.asset.meta new file mode 100644 index 000000000..f15dc3663 --- /dev/null +++ b/sdkproject/Assets/Mapbox/Examples/1_DataExplorer/BuildingCollection.asset.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: f11fd5ce183f93846858df725d8eab7b +timeCreated: 1519743585 +licenseType: Pro +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdkproject/Assets/Mapbox/Examples/1_DataExplorer/HighlightBuildings.cs b/sdkproject/Assets/Mapbox/Examples/1_DataExplorer/HighlightBuildings.cs new file mode 100644 index 000000000..3ecea65f4 --- /dev/null +++ b/sdkproject/Assets/Mapbox/Examples/1_DataExplorer/HighlightBuildings.cs @@ -0,0 +1,36 @@ +namespace Mapbox.Examples +{ + using KDTree; + using UnityEngine; + using Mapbox.Unity.MeshGeneration; + using Mapbox.Unity.MeshGeneration.Data; + + public class HighlightBuildings : MonoBehaviour + { + public KdTreeCollection Collection; + public int MaxCount = 100; + public float Range = 10; + Ray ray; + Plane groundPlane = new Plane(Vector3.up, Vector3.zero); + Vector3 pos; + float rayDistance; + private NearestNeighbour pIter; + + void Update() + { + if (Input.GetMouseButton(0)) + { + ray = Camera.main.ScreenPointToRay(Input.mousePosition); + if (groundPlane.Raycast(ray, out rayDistance)) + { + pos = ray.GetPoint(rayDistance); + pIter = Collection.NearestNeighbors(new double[] { pos.x, pos.z }, MaxCount, Range); + while (pIter.MoveNext()) + { + pIter.Current.Transform.localScale = Vector3.zero; + } + } + } + } + } +} diff --git a/sdkproject/Assets/Mapbox/Examples/1_DataExplorer/HighlightBuildings.cs.meta b/sdkproject/Assets/Mapbox/Examples/1_DataExplorer/HighlightBuildings.cs.meta new file mode 100644 index 000000000..fb9d8b226 --- /dev/null +++ b/sdkproject/Assets/Mapbox/Examples/1_DataExplorer/HighlightBuildings.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: f64621ccb8a60114c83bbd39e5fdc145 +timeCreated: 1522435864 +licenseType: Pro +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdkproject/Assets/Mapbox/Examples/1_DataExplorer/InteractiveStyledVectorMap.unity b/sdkproject/Assets/Mapbox/Examples/1_DataExplorer/InteractiveStyledVectorMap.unity index b128909df..0392eb5b1 100644 --- a/sdkproject/Assets/Mapbox/Examples/1_DataExplorer/InteractiveStyledVectorMap.unity +++ b/sdkproject/Assets/Mapbox/Examples/1_DataExplorer/InteractiveStyledVectorMap.unity @@ -516,12 +516,13 @@ MonoBehaviour: colorPallete: {fileID: 11400000, guid: e1b79d6caac0144adada0c548ef06705, type: 2} buildingsWithUniqueIds: 0 - moveFeaturePositionTo: 0 + moveFeaturePositionTo: 2 MeshModifiers: [] GoModifiers: - {fileID: 11400000, guid: b7b664c5d0b1dfd4ea8bf239a71912ef, type: 2} - {fileID: 11400000, guid: 14ec972a1cec2dc429f5eca7d22b572f, type: 2} - {fileID: 11400000, guid: aa354e89f869a4db0a6c3ceb3c79d0be, type: 2} + - {fileID: 11400000, guid: d534c6a861f735c4dab3990ac3c05158, type: 2} - coreOptions: isActive: 1 sublayerName: Parks @@ -1524,6 +1525,7 @@ GameObject: - component: {fileID: 1351042190} - component: {fileID: 1351042187} - component: {fileID: 1351042188} + - component: {fileID: 1351042189} m_Layer: 0 m_Name: Main Camera m_TagString: MainCamera @@ -1557,6 +1559,20 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: ShadowDistance: 1000 +--- !u!114 &1351042189 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1351042186} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f64621ccb8a60114c83bbd39e5fdc145, type: 3} + m_Name: + m_EditorClassIdentifier: + Collection: {fileID: 11400000, guid: f11fd5ce183f93846858df725d8eab7b, type: 2} + MaxCount: 100 + Range: 10 --- !u!81 &1351042190 AudioListener: m_ObjectHideFlags: 0 @@ -1793,7 +1809,7 @@ Prefab: - target: {fileID: 224907993215804030, guid: 98be219873e6d4dffb5949746f515a33, type: 2} propertyPath: m_AnchorMin.y - value: 0 + value: 0.000000059604645 objectReference: {fileID: 0} - target: {fileID: 224907993215804030, guid: 98be219873e6d4dffb5949746f515a33, type: 2} @@ -1823,7 +1839,7 @@ Prefab: - target: {fileID: 224835413515161394, guid: 98be219873e6d4dffb5949746f515a33, type: 2} propertyPath: m_AnchoredPosition.y - value: -68.92136 + value: -70.363434 objectReference: {fileID: 0} - target: {fileID: 224835413515161394, guid: 98be219873e6d4dffb5949746f515a33, type: 2} @@ -1833,12 +1849,12 @@ Prefab: - target: {fileID: 224835413515161394, guid: 98be219873e6d4dffb5949746f515a33, type: 2} propertyPath: m_SizeDelta.y - value: 117.84271 + value: 120.72687 objectReference: {fileID: 0} - target: {fileID: 224857786874416376, guid: 98be219873e6d4dffb5949746f515a33, type: 2} propertyPath: m_SizeDelta.y - value: 137.84271 + value: 0 objectReference: {fileID: 0} - target: {fileID: 224907856650798614, guid: 98be219873e6d4dffb5949746f515a33, type: 2} @@ -1889,7 +1905,7 @@ Prefab: - target: {fileID: 224857786874416376, guid: 98be219873e6d4dffb5949746f515a33, type: 2} propertyPath: m_AnchoredPosition.y - value: 0.000015258789 + value: -0.000015258789 objectReference: {fileID: 0} - target: {fileID: 114731470950229392, guid: 98be219873e6d4dffb5949746f515a33, type: 2} diff --git a/sdkproject/Assets/Mapbox/Examples/_sharedModules/AddToTreeCollectionModifier.asset b/sdkproject/Assets/Mapbox/Examples/_sharedModules/AddToTreeCollectionModifier.asset new file mode 100644 index 000000000..b2cadb311 --- /dev/null +++ b/sdkproject/Assets/Mapbox/Examples/_sharedModules/AddToTreeCollectionModifier.asset @@ -0,0 +1,15 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4538b91da572dfa41adf689573eaba4b, type: 3} + m_Name: AddToTreeCollectionModifier + m_EditorClassIdentifier: + Active: 1 + _collection: {fileID: 11400000, guid: f11fd5ce183f93846858df725d8eab7b, type: 2} diff --git a/sdkproject/Assets/Mapbox/Examples/_sharedModules/AddToTreeCollectionModifier.asset.meta b/sdkproject/Assets/Mapbox/Examples/_sharedModules/AddToTreeCollectionModifier.asset.meta new file mode 100644 index 000000000..3b7a0324c --- /dev/null +++ b/sdkproject/Assets/Mapbox/Examples/_sharedModules/AddToTreeCollectionModifier.asset.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: d534c6a861f735c4dab3990ac3c05158 +timeCreated: 1519740059 +licenseType: Pro +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdkproject/Assets/Mapbox/Examples/_sharedModules/TransparentGreyPolygonStack.asset b/sdkproject/Assets/Mapbox/Examples/_sharedModules/TransparentGreyPolygonStack.asset index 342133914..186057708 100644 --- a/sdkproject/Assets/Mapbox/Examples/_sharedModules/TransparentGreyPolygonStack.asset +++ b/sdkproject/Assets/Mapbox/Examples/_sharedModules/TransparentGreyPolygonStack.asset @@ -15,8 +15,7 @@ MonoBehaviour: MeshModifiers: - {fileID: 11400000, guid: 4fe5c136889ae0347af431be7e59e489, type: 2} - {fileID: 11400000, guid: b432bf85b0df280468dcfdaf5a9b90d0, type: 2} - - {fileID: 11400000, guid: 97f59348b2a022d4eb425de65a1f8ce5, type: 2} - - {fileID: 11400000, guid: c1c619f43a2cb5b4a8cbc80e9e814804, type: 2} + - {fileID: 11400000, guid: a49403503b2b1534f832833cbd477437, type: 2} GoModifiers: - - {fileID: 11400000, guid: 85d7acaaafb33410dbbc83cfad604f86, type: 2} - - {fileID: 11400000, guid: af0e85fdc76cf4bceb25d8ad0bfa2ace, type: 2} + - {fileID: 11400000, guid: f79ee57901cdf483e87c86d4ce3e7247, type: 2} + - {fileID: 11400000, guid: d534c6a861f735c4dab3990ac3c05158, type: 2} diff --git a/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/FeatureCollectionBase.cs b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/FeatureCollectionBase.cs new file mode 100644 index 000000000..1592df2cc --- /dev/null +++ b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/FeatureCollectionBase.cs @@ -0,0 +1,19 @@ +namespace Mapbox.Unity.MeshGeneration +{ + using Mapbox.Unity.MeshGeneration.Data; + using UnityEngine; + + public class FeatureCollectionBase : ScriptableObject + { + public virtual void Initialize() + { + + } + + public virtual void AddFeature(double[] position, VectorEntity ve) + { + + } + + } +} \ No newline at end of file diff --git a/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/FeatureCollectionBase.cs.meta b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/FeatureCollectionBase.cs.meta new file mode 100644 index 000000000..302e48449 --- /dev/null +++ b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/FeatureCollectionBase.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 4d7c8347fa9db194e99888f8337ed5e2 +timeCreated: 1516628160 +licenseType: Pro +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree.meta b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree.meta new file mode 100644 index 000000000..f0251aa19 --- /dev/null +++ b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 4f15de9092560a644913bb912f5642ec +folderAsset: yes +timeCreated: 1519738933 +licenseType: Pro +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/DistanceFunctions.cs b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/DistanceFunctions.cs new file mode 100644 index 000000000..84fae2278 --- /dev/null +++ b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/DistanceFunctions.cs @@ -0,0 +1,77 @@ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace KDTree +{ + /// + /// An interface which enables flexible distance functions. + /// + public interface DistanceFunctions + { + /// + /// Compute a distance between two n-dimensional points. + /// + /// The first point. + /// The second point. + /// The n-dimensional distance. + double Distance(double[] p1, double[] p2); + + /// + /// Find the shortest distance from a point to an axis aligned rectangle in n-dimensional space. + /// + /// The point of interest. + /// The minimum coordinate of the rectangle. + /// The maximum coorindate of the rectangle. + /// The shortest n-dimensional distance between the point and rectangle. + double DistanceToRectangle(double[] point, double[] min, double[] max); + } + + /// + /// A distance function for our KD-Tree which returns squared euclidean distances. + /// + public class SquareEuclideanDistanceFunction : DistanceFunctions + { + /// + /// Find the squared distance between two n-dimensional points. + /// + /// The first point. + /// The second point. + /// The n-dimensional squared distance. + public double Distance(double[] p1, double[] p2) + { + double fSum = 0; + for (int i = 0; i < p1.Length; i++) + { + double fDifference = (p1[i] - p2[i]); + fSum += fDifference * fDifference; + } + return fSum; + } + + /// + /// Find the shortest distance from a point to an axis aligned rectangle in n-dimensional space. + /// + /// The point of interest. + /// The minimum coordinate of the rectangle. + /// The maximum coorindate of the rectangle. + /// The shortest squared n-dimensional squared distance between the point and rectangle. + public double DistanceToRectangle(double[] point, double[] min, double[] max) + { + double fSum = 0; + double fDifference = 0; + for (int i = 0; i < point.Length; ++i) + { + fDifference = 0; + if (point[i] > max[i]) + fDifference = (point[i] - max[i]); + else if (point[i] < min[i]) + fDifference = (point[i] - min[i]); + fSum += fDifference * fDifference; + } + return fSum; + } + } +} diff --git a/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/DistanceFunctions.cs.meta b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/DistanceFunctions.cs.meta new file mode 100644 index 000000000..d79dbe794 --- /dev/null +++ b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/DistanceFunctions.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 1a40f078f2196e048b6b95fcc9738e90 +timeCreated: 1516636092 +licenseType: Pro +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/IntervalHeap.cs b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/IntervalHeap.cs new file mode 100644 index 000000000..a7a20b328 --- /dev/null +++ b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/IntervalHeap.cs @@ -0,0 +1,474 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace KDTree +{ + /// + /// A binary interval heap is double-ended priority queue is a priority queue that it allows + /// for efficient removal of both the maximum and minimum element. + /// + /// The data type contained at each key. + /// This is based on this: https://bitbucket.org/rednaxela/knn-benchmark/src/tip/ags/utils/dataStructures/trees/thirdGenKD/ + public class IntervalHeap + { + /// + /// The default size for a new interval heap. + /// + private const int DEFAULT_SIZE = 64; + + /// + /// The internal data array which contains the stored objects. + /// + private T[] tData; + + /// + /// The array of keys which + /// + private double[] tKeys; + + /// + /// Construct a new interval heap with the default capacity. + /// + public IntervalHeap() : this(DEFAULT_SIZE) + { + } + + /// + /// Construct a new interval heap with a custom capacity. + /// + /// + public IntervalHeap(int capacity) + { + this.tData = new T[capacity]; + this.tKeys = new double[capacity]; + this.Capacity = capacity; + this.Size = 0; + } + + /// + /// The number of items in this interval heap. + /// + public int Size { get; private set; } + + /// + /// The current capacity of this interval heap. + /// + public int Capacity { get; private set; } + + /// + /// Get the data with the smallest key. + /// + public T Min + { + get + { + if (Size == 0) + throw new Exception(); + return tData[0]; + } + } + + /// + /// Get the data with the largest key. + /// + public T Max + { + get + { + if (Size == 0) + { + throw new Exception(); + } + else if (Size == 1) + { + return tData[0]; + } + + return tData[1]; + } + } + + /// + /// Get the smallest key. + /// + public double MinKey + { + get + { + if (Size == 0) + throw new Exception(); + return tKeys[0]; + } + } + + /// + /// Get the largest key. + /// + public double MaxKey + { + get + { + if (Size == 0) + { + throw new Exception(); + } + else if (Size == 1) + { + return tKeys[0]; + } + + return tKeys[1]; + } + } + + /// + /// Insert a new data item at a given key. + /// + /// The value which represents our data (i.e. a distance). + /// The data we want to store. + public void Insert(double key, T value) + { + // If more room is needed, double the array size. + if (Size >= Capacity) + { + // Double the capacity. + Capacity *= 2; + + // Expand the data array. + var newData = new T[Capacity]; + Array.Copy(tData, newData, tData.Length); + tData = newData; + + // Expand the key array. + var newKeys = new double[Capacity]; + Array.Copy(tKeys, newKeys, tKeys.Length); + tKeys = newKeys; + } + + // Insert the new value at the end. + Size++; + tData[Size-1] = value; + tKeys[Size-1] = key; + + // Ensure it is in the right place. + SiftInsertedValueUp(); + } + + /// + /// Remove the item with the smallest key from the queue. + /// + public void RemoveMin() + { + // Check for errors. + if (Size == 0) + throw new Exception(); + + // Remove the item by + Size--; + tData[0] = tData[Size]; + tKeys[0] = tKeys[Size]; + tData[Size] = default(T); + SiftDownMin(0); + } + + /// + /// Replace the item with the smallest key in the queue. + /// + /// The new minimum key. + /// The new minumum data value. + public void ReplaceMin(double key, T value) + { + // Check for errors. + if (Size == 0) + throw new Exception(); + + // Add the data. + tData[0] = value; + tKeys[0] = key; + + // If we have more than one item. + if (Size > 1) + { + // Swap with pair if necessary. + if (tKeys[1] < key) + Swap(0, 1); + SiftDownMin(0); + } + } + + /// + /// Remove the item with the largest key in the queue. + /// + public void RemoveMax() + { + // If we have no items in the queue. + if (Size == 0) + { + throw new Exception(); + } + + // If we have one item, remove the min. + else if (Size == 1) + { + RemoveMin(); + return; + } + + // Remove the max. + Size--; + tData[1] = tData[Size]; + tKeys[1] = tKeys[Size]; + tData[Size] = default(T); + SiftDownMax(1); + } + + /// + /// Swap out the item with the largest key in the queue. + /// + /// The new key for the largest item. + /// The new data for the largest item. + public void ReplaceMax(double key, T value) + { + if (Size == 0) + { + throw new Exception(); + } + else if (Size == 1) + { + ReplaceMin(key, value); + return; + } + + tData[1] = value; + tKeys[1] = key; + // Swap with pair if necessary + if (key < tKeys[0]) { + Swap(0, 1); + } + SiftDownMax(1); + } + + + /// + /// Internal helper method which swaps two values in the arrays. + /// This swaps both data and key entries. + /// + /// The first index. + /// The second index. + /// The second index. + private int Swap(int x, int y) + { + // Store temp. + T yData = tData[y]; + double yDist = tKeys[y]; + + // Swap + tData[y] = tData[x]; + tKeys[y] = tKeys[x]; + tData[x] = yData; + tKeys[x] = yDist; + + // Return. + return y; + } + + /** + * Min-side (u % 2 == 0): + * - leftchild: 2u + 2 + * - rightchild: 2u + 4 + * - parent: (x/2-1)&~1 + * + * Max-side (u % 2 == 1): + * - leftchild: 2u + 1 + * - rightchild: 2u + 3 + * - parent: (x/2-1)|1 + */ + + /// + /// Place a newly inserted element a into the correct tree position. + /// + private void SiftInsertedValueUp() + { + // Work out where the element was inserted. + int u = Size-1; + + // If it is the only element, nothing to do. + if (u == 0) + { + } + + // If it is the second element, sort with it's pair. + else if (u == 1) + { + // Swap if less than paired item. + if (tKeys[u] < tKeys[u-1]) + Swap(u, u-1); + } + + // If it is on the max side, + else if (u % 2 == 1) + { + // Already paired. Ensure pair is ordered right + int p = (u/2-1)|1; // The larger value of the parent pair + if (tKeys[u] < tKeys[u-1]) + { // If less than it's pair + u = Swap(u, u-1); // Swap with it's pair + if (tKeys[u] < tKeys[p-1]) + { // If smaller than smaller parent pair + // Swap into min-heap side + u = Swap(u, p-1); + SiftUpMin(u); + } + } + else + { + if (tKeys[u] > tKeys[p]) + { // If larger that larger parent pair + // Swap into max-heap side + u = Swap(u, p); + SiftUpMax(u); + } + } + } + else + { + // Inserted in the lower-value slot without a partner + int p = (u/2-1)|1; // The larger value of the parent pair + if (tKeys[u] > tKeys[p]) + { // If larger that larger parent pair + // Swap into max-heap side + u = Swap(u, p); + SiftUpMax(u); + } + else if (tKeys[u] < tKeys[p-1]) + { // If smaller than smaller parent pair + // Swap into min-heap side + u = Swap(u, p-1); + SiftUpMin(u); + } + } + } + + /// + /// Bubble elements up the min side of the tree. + /// + /// The child index. + private void SiftUpMin(int iChild) + { + // Min-side parent: (x/2-1)&~1 + for (int iParent = (iChild/2-1)&~1; + iParent >= 0 && tKeys[iChild] < tKeys[iParent]; + iChild = iParent, iParent = (iChild/2-1)&~1) + { + Swap(iChild, iParent); + } + } + + /// + /// Bubble elements up the max side of the tree. + /// + /// The child index. + private void SiftUpMax(int iChild) + { + // Max-side parent: (x/2-1)|1 + for (int iParent = (iChild/2-1)|1; + iParent >= 0 && tKeys[iChild] > tKeys[iParent]; + iChild = iParent, iParent = (iChild/2-1)|1) + { + Swap(iChild, iParent); + } + } + + /// + /// Bubble elements down the min side of the tree. + /// + /// The parent index. + private void SiftDownMin(int iParent) + { + // For each child of the parent. + for (int iChild = iParent * 2 + 2; iChild < Size; iParent = iChild, iChild = iParent * 2 + 2) + { + // If the next child is less than the current child, select the next one. + if (iChild + 2 < Size && tKeys[iChild + 2] < tKeys[iChild]) + { + iChild += 2; + } + + // If it is less than our parent swap. + if (tKeys[iChild] < tKeys[iParent]) + { + Swap(iParent, iChild); + + // Swap the pair if necessary. + if (iChild+1 < Size && tKeys[iChild+1] < tKeys[iChild]) + { + Swap(iChild, iChild+1); + } + } + else + { + break; + } + } + } + + /// + /// Bubble elements down the max side of the tree. + /// + /// + private void SiftDownMax(int iParent) + { + // For each child on the max side of the tree. + for (int iChild = iParent * 2 + 1; iChild <= Size; iParent = iChild, iChild = iParent * 2 + 1) + { + // If the child is the last one (and only has half a pair). + if (iChild == Size) + { + // CHeck if we need to swap with th parent. + if (tKeys[iChild - 1] > tKeys[iParent]) + Swap(iParent, iChild - 1); + break; + } + + // If there is only room for a right child lower pair. + else if (iChild + 2 == Size) + { + // Swap the children. + if (tKeys[iChild + 1] > tKeys[iChild]) + { + // Swap with the parent. + if (tKeys[iChild + 1] > tKeys[iParent]) + Swap(iParent, iChild + 1); + break; + } + } + + // + else if (iChild + 2 < Size) + { + // If there is room for a right child upper pair + if (tKeys[iChild + 2] > tKeys[iChild]) + { + iChild += 2; + } + } + if (tKeys[iChild] > tKeys[iParent]) + { + Swap(iParent, iChild); + // Swap with pair if necessary + if (tKeys[iChild-1] > tKeys[iChild]) + { + Swap(iChild, iChild-1); + } + } + else + { + break; + } + } + } + } +} \ No newline at end of file diff --git a/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/IntervalHeap.cs.meta b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/IntervalHeap.cs.meta new file mode 100644 index 000000000..c08ea545e --- /dev/null +++ b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/IntervalHeap.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 1e090ab286001c3488cf724a6a3325cf +timeCreated: 1519738987 +licenseType: Pro +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/KDNode.cs b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/KDNode.cs new file mode 100644 index 000000000..75e486bff --- /dev/null +++ b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/KDNode.cs @@ -0,0 +1,301 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace KDTree +{ + /// + /// A KD-Tree node which supports a generic number of dimensions. All data items + /// need the same number of dimensions. + /// This node splits based on the largest range of any dimension. + /// + /// The generic data type this structure contains. + /// This is based on this: https://bitbucket.org/rednaxela/knn-benchmark/src/tip/ags/utils/dataStructures/trees/thirdGenKD/ + public class KDNode + { + #region Internal properties and constructor + // All types + /// + /// The number of dimensions for this node. + /// + protected internal int iDimensions; + + /// + /// The maximum capacity of this node. + /// + protected internal int iBucketCapacity; + + // Leaf only + /// + /// The array of locations. [index][dimension] + /// + protected internal double[][] tPoints; + + /// + /// The array of data values. [index] + /// + protected internal T[] tData; + + // Stem only + /// + /// The left and right children. + /// + protected internal KDNode pLeft, pRight; + /// + /// The split dimension. + /// + protected internal int iSplitDimension; + /// + /// The split value (larger go into the right, smaller go into left) + /// + protected internal double fSplitValue; + + // Bounds + /// + /// The min and max bound for this node. All dimensions. + /// + protected internal double[] tMinBound, tMaxBound; + + /// + /// Does this node represent only one point. + /// + protected internal bool bSinglePoint; + + /// + /// Protected method which constructs a new KDNode. + /// + /// The number of dimensions for this node (all the same in the tree). + /// The initial capacity of the bucket. + protected KDNode(int iDimensions, int iBucketCapacity) + { + // Variables. + this.iDimensions = iDimensions; + this.iBucketCapacity = iBucketCapacity; + this.Size = 0; + this.bSinglePoint = true; + + // Setup leaf elements. + this.tPoints = new double[iBucketCapacity+1][]; + this.tData = new T[iBucketCapacity+1]; + } + #endregion + + #region External Operations + /// + /// The number of items in this leaf node and all children. + /// + public int Size { get; private set; } + + /// + /// Is this KDNode a leaf or not? + /// + public bool IsLeaf { get { return tPoints != null; } } + + /// + /// Insert a new point into this leaf node. + /// + /// The position which represents the data. + /// The value of the data. + public void AddPoint(double[] tPoint, T kValue) + { + // Find the correct leaf node. + KDNode pCursor = this; + while (!pCursor.IsLeaf) + { + // Extend the size of the leaf. + pCursor.ExtendBounds(tPoint); + pCursor.Size++; + + // If it is larger select the right, or lower, select the left. + if (tPoint[pCursor.iSplitDimension] > pCursor.fSplitValue) + { + pCursor = pCursor.pRight; + } + else + { + pCursor = pCursor.pLeft; + } + } + + // Insert it into the leaf. + pCursor.AddLeafPoint(tPoint, kValue); + } + #endregion + + #region Internal Operations + /// + /// Insert the point into the leaf. + /// + /// The point to insert the data at. + /// The value at the point. + private void AddLeafPoint(double[] tPoint, T kValue) + { + // Add the data point to this node. + tPoints[Size] = tPoint; + tData[Size] = kValue; + ExtendBounds(tPoint); + Size++; + + // Split if the node is getting too large in terms of data. + if (Size == tPoints.Length - 1) + { + // If the node is getting too physically large. + if (CalculateSplit()) + { + // If the node successfully had it's split value calculated, split node. + SplitLeafNode(); + } + else + { + // If the node could not be split, enlarge node data capacity. + IncreaseLeafCapacity(); + } + } + } + + /// + /// If the point lies outside the boundaries, return false else true. + /// + /// The point. + /// True if the point is inside the boundaries, false outside. + private bool CheckBounds(double[] tPoint) + { + for (int i = 0; i < iDimensions; ++i) + { + if (tPoint[i] > tMaxBound[i]) return false; + if (tPoint[i] < tMinBound[i]) return false; + } + return true; + } + + /// + /// Extend this node to contain a new point. + /// + /// The point to contain. + private void ExtendBounds(double[] tPoint) + { + // If we don't have bounds, create them using the new point then bail. + if (tMinBound == null) + { + tMinBound = new double[iDimensions]; + tMaxBound = new double[iDimensions]; + Array.Copy(tPoint, tMinBound, iDimensions); + Array.Copy(tPoint, tMaxBound, iDimensions); + return; + } + + // For each dimension. + for (int i = 0; i < iDimensions; ++i) + { + if (Double.IsNaN(tPoint[i])) + { + if (!Double.IsNaN(tMinBound[i]) || !Double.IsNaN(tMaxBound[i])) + bSinglePoint = false; + + tMinBound[i] = Double.NaN; + tMaxBound[i] = Double.NaN; + } + else if (tMinBound[i] > tPoint[i]) + { + tMinBound[i] = tPoint[i]; + bSinglePoint = false; + } + else if (tMaxBound[i] < tPoint[i]) + { + tMaxBound[i] = tPoint[i]; + bSinglePoint = false; + } + } + } + + /// + /// Double the capacity of this leaf. + /// + private void IncreaseLeafCapacity() + { + Array.Resize(ref tPoints, tPoints.Length * 2); + Array.Resize(ref tData, tData.Length * 2); + } + + /// + /// Work out if this leaf node should split. If it should, a new split value and dimension is calculated + /// based on the dimension with the largest range. + /// + /// True if the node split, false if not. + private bool CalculateSplit() + { + // Don't split if we are just one point. + if (bSinglePoint) + return false; + + // Find the dimension with the largest range. This will be our split dimension. + double fWidth = 0; + for (int i = 0; i < iDimensions; i++) + { + double fDelta = (tMaxBound[i] - tMinBound[i]); + if (Double.IsNaN(fDelta)) + fDelta = 0; + + if (fDelta > fWidth) + { + iSplitDimension = i; + fWidth = fDelta; + } + } + + // If we are not wide (i.e. all the points are in one place), don't split. + if (fWidth == 0) + return false; + + // Split in the middle of the node along the widest dimension. + fSplitValue = (tMinBound[iSplitDimension] + tMaxBound[iSplitDimension]) * 0.5; + + // Never split on infinity or NaN. + if (fSplitValue == Double.PositiveInfinity) + fSplitValue = Double.MaxValue; + else if (fSplitValue == Double.NegativeInfinity) + fSplitValue = Double.MinValue; + + // Don't let the split value be the same as the upper value as + // can happen due to rounding errors! + if (fSplitValue == tMaxBound[iSplitDimension]) + fSplitValue = tMinBound[iSplitDimension]; + + // Success + return true; + } + + /// + /// Split this leaf node by creating left and right children, then moving all the children of + /// this node into the respective buckets. + /// + private void SplitLeafNode() + { + // Create the new children. + pRight = new KDNode(iDimensions, iBucketCapacity); + pLeft = new KDNode(iDimensions, iBucketCapacity); + + // Move each item in this leaf into the children. + for (int i = 0; i < Size; ++i) + { + // Store. + double[] tOldPoint = tPoints[i]; + T kOldData = tData[i]; + + // If larger, put it in the right. + if (tOldPoint[iSplitDimension] > fSplitValue) + pRight.AddLeafPoint(tOldPoint, kOldData); + + // If smaller, put it in the left. + else + pLeft.AddLeafPoint(tOldPoint, kOldData); + } + + // Wipe the data from this KDNode. + tPoints = null; + tData = null; + } + #endregion + } +} diff --git a/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/KDNode.cs.meta b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/KDNode.cs.meta new file mode 100644 index 000000000..1418c53ef --- /dev/null +++ b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/KDNode.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: ec5d72f02adf6254aad038350ce771a3 +timeCreated: 1516636093 +licenseType: Pro +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/KDTree.cs b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/KDTree.cs new file mode 100644 index 000000000..5c739e896 --- /dev/null +++ b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/KDTree.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace KDTree +{ + + /// + /// A KDTree class represents the root of a variable-dimension KD-Tree. + /// + /// The generic data type we want this tree to contain. + /// This is based on this: https://bitbucket.org/rednaxela/knn-benchmark/src/tip/ags/utils/dataStructures/trees/thirdGenKD/ + public class KDTree : KDNode + { + /// + /// Create a new KD-Tree given a number of dimensions. + /// + /// The number of data sorting dimensions. i.e. 3 for a 3D point. + public KDTree(int iDimensions) + : base(iDimensions, 24) + { + } + + /// + /// Create a new KD-Tree given a number of dimensions and initial bucket capacity. + /// + /// The number of data sorting dimensions. i.e. 3 for a 3D point. + /// The default number of items that can be stored in each node. + public KDTree(int iDimensions, int iBucketCapacity) + : base(iDimensions, iBucketCapacity) + { + } + + /// + /// Get the nearest neighbours to a point in the kd tree using a square euclidean distance function. + /// + /// The point of interest. + /// The maximum number of points which can be returned by the iterator. + /// A threshold distance to apply. Optional. Negative values mean that it is not applied. + /// A new nearest neighbour iterator with the given parameters. + public NearestNeighbour NearestNeighbors(double[] tSearchPoint, int iMaxReturned, double fDistance = -1) + { + DistanceFunctions distanceFunction = new SquareEuclideanDistanceFunction(); + return NearestNeighbors(tSearchPoint, distanceFunction, iMaxReturned, fDistance); + } + + /// + /// Get the nearest neighbours to a point in the kd tree using a user defined distance function. + /// + /// The point of interest. + /// The maximum number of points which can be returned by the iterator. + /// The distance function to use. + /// A threshold distance to apply. Optional. Negative values mean that it is not applied. + /// A new nearest neighbour iterator with the given parameters. + public NearestNeighbour NearestNeighbors(double[] tSearchPoint, DistanceFunctions kDistanceFunction, int iMaxReturned, double fDistance) + { + return new NearestNeighbour(this, tSearchPoint, kDistanceFunction, iMaxReturned, fDistance); + } + } +} \ No newline at end of file diff --git a/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/KDTree.cs.meta b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/KDTree.cs.meta new file mode 100644 index 000000000..6ebec71f1 --- /dev/null +++ b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/KDTree.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: c297650f28b0efc4d97523269824786d +timeCreated: 1516636093 +licenseType: Pro +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/MinHeap.cs b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/MinHeap.cs new file mode 100644 index 000000000..721777f43 --- /dev/null +++ b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/MinHeap.cs @@ -0,0 +1,189 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace KDTree +{ + /// + /// A MinHeap is a smallest-first queue based around a binary heap so it is quick to insert / remove items. + /// + /// The type of data this MinHeap stores. + /// This is based on this: https://bitbucket.org/rednaxela/knn-benchmark/src/tip/ags/utils/dataStructures/trees/thirdGenKD/ + public class MinHeap + { + /// + /// The default size for a min heap. + /// + private static int DEFAULT_SIZE = 64; + + /// + /// The data array. This stores the data items in the heap. + /// + private T[] tData; + + /// + /// The key array. This determines how items are ordered. Smallest first. + /// + private double[] tKeys; + + /// + /// Create a new min heap with the default capacity. + /// + public MinHeap() : this(DEFAULT_SIZE) + { + } + + /// + /// Create a new min heap with a given capacity. + /// + /// + public MinHeap(int iCapacity) + { + this.tData = new T[iCapacity]; + this.tKeys = new double[iCapacity]; + this.Capacity = iCapacity; + this.Size = 0; + } + + /// + /// The number of items in this queue. + /// + public int Size { get; private set; } + + /// + /// The amount of space in this queue. + /// + public int Capacity { get; private set; } + + /// + /// Insert a new element. + /// + /// The key which represents its position in the priority queue (ie. distance). + /// The value to be stored at the key. + public void Insert(double key, T value) + { + // If we need more room, double the space. + if (Size >= Capacity) + { + // Calcualte the new capacity. + Capacity *= 2; + + // Copy the data array. + var newData = new T[Capacity]; + Array.Copy(tData, newData, tData.Length); + tData = newData; + + // Copy the key array. + var newKeys = new double[Capacity]; + Array.Copy(tKeys, newKeys, tKeys.Length); + tKeys = newKeys; + } + + // Insert new value at the end + tData[Size] = value; + tKeys[Size] = key; + SiftUp(Size); + Size++; + } + + /// + /// Remove the smallest element. + /// + public void RemoveMin() + { + if (Size == 0) + throw new Exception(); + + Size--; + tData[0] = tData[Size]; + tKeys[0] = tKeys[Size]; + tData[Size] = default(T); + SiftDown(0); + } + + /// + /// Get the data stored at the minimum element. + /// + public T Min + { + get + { + if (Size == 0) + throw new Exception(); + + return tData[0]; + } + } + + /// + /// Get the key which represents the minimum element. + /// + public double MinKey + { + get + { + if (Size == 0) + throw new Exception(); + + return tKeys[0]; + } + } + + /// + /// Bubble a child item up the tree. + /// + /// + private void SiftUp(int iChild) + { + // For each parent above the child, if the parent is smaller then bubble it up. + for (int iParent = (iChild - 1) / 2; + iChild != 0 && tKeys[iChild] < tKeys[iParent]; + iChild = iParent, iParent = (iChild - 1) / 2) + { + T kData = tData[iParent]; + double dDist = tKeys[iParent]; + + tData[iParent] = tData[iChild]; + tKeys[iParent] = tKeys[iChild]; + + tData[iChild] = kData; + tKeys[iChild] = dDist; + } + } + + /// + /// Bubble a parent down through the children so it goes in the right place. + /// + /// The index of the parent. + private void SiftDown(int iParent) + { + // For each child. + for (int iChild = iParent * 2 + 1; iChild < Size; iParent = iChild, iChild = iParent * 2 + 1) + { + // If the child is larger, select the next child. + if (iChild + 1 < Size && tKeys[iChild] > tKeys[iChild + 1]) + iChild++; + + // If the parent is larger than the largest child, swap. + if (tKeys[iParent] > tKeys[iChild]) + { + // Swap the points + T pData = tData[iParent]; + double pDist = tKeys[iParent]; + + tData[iParent] = tData[iChild]; + tKeys[iParent] = tKeys[iChild]; + + tData[iChild] = pData; + tKeys[iChild] = pDist; + } + + // TODO: REMOVE THE BREAK + else + { + break; + } + } + } + } +} \ No newline at end of file diff --git a/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/MinHeap.cs.meta b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/MinHeap.cs.meta new file mode 100644 index 000000000..497398cbe --- /dev/null +++ b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/MinHeap.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: b3dd3caca8a74224cab6c8dc36751097 +timeCreated: 1516636092 +licenseType: Pro +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/NearestNeighbour.cs b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/NearestNeighbour.cs new file mode 100644 index 000000000..59282ec47 --- /dev/null +++ b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/NearestNeighbour.cs @@ -0,0 +1,248 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace KDTree +{ + /// + /// A NearestNeighbour iterator for the KD-tree which intelligently iterates and captures relevant data in the search space. + /// + /// The type of data the iterator should handle. + public class NearestNeighbour : IEnumerator, IEnumerable + { + /// The point from which are searching in n-dimensional space. + private double[] tSearchPoint; + /// A distance function which is used to compare nodes and value positions. + private DistanceFunctions kDistanceFunction; + /// The tree nodes which have yet to be evaluated. + private MinHeap> pPending; + /// The values which have been evaluated and selected. + private IntervalHeap pEvaluated; + /// The root of the kd tree to begin searching from. + private KDNode pRoot = null; + + /// The max number of points we can return through this iterator. + private int iMaxPointsReturned = 0; + /// The number of points we can still test before conclusion. + private int iPointsRemaining; + /// Threshold to apply to tree iteration. Negative numbers mean no threshold applied. + private double fThreshold; + + /// Current value distance. + private double _CurrentDistance = -1; + /// Current value reference. + private T _Current = default(T); + + /// + /// Construct a new nearest neighbour iterator. + /// + /// The root of the tree to begin searching from. + /// The point in n-dimensional space to search. + /// The distance function used to evaluate the points. + /// The max number of points which can be returned by this iterator. Capped to max in tree. + /// Threshold to apply to the search space. Negative numbers indicate that no threshold is applied. + public NearestNeighbour(KDNode pRoot, double[] tSearchPoint, DistanceFunctions kDistance, int iMaxPoints, double fThreshold) + { + // Check the dimensionality of the search point. + if (tSearchPoint.Length != pRoot.iDimensions) + throw new Exception("Dimensionality of search point and kd-tree are not the same."); + + // Store the search point. + this.tSearchPoint = new double[tSearchPoint.Length]; + Array.Copy(tSearchPoint, this.tSearchPoint, tSearchPoint.Length); + + // Store the point count, distance function and tree root. + this.iPointsRemaining = Math.Min(iMaxPoints, pRoot.Size); + this.fThreshold = fThreshold; + this.kDistanceFunction = kDistance; + this.pRoot = pRoot; + this.iMaxPointsReturned = iMaxPoints; + _CurrentDistance = -1; + + // Create an interval heap for the points we check. + this.pEvaluated = new IntervalHeap(); + + // Create a min heap for the things we need to check. + this.pPending = new MinHeap>(); + this.pPending.Insert(0, pRoot); + } + + /// + /// Check for the next iterator item. + /// + /// True if we have one, false if not. + public bool MoveNext() + { + // Bail if we are finished. + if (iPointsRemaining == 0) + { + _Current = default(T); + return false; + } + + // While we still have paths to evaluate. + while (pPending.Size > 0 && (pEvaluated.Size == 0 || (pPending.MinKey < pEvaluated.MinKey))) + { + // If there are pending paths possibly closer than the nearest evaluated point, check it out + KDNode pCursor = pPending.Min; + pPending.RemoveMin(); + + // Descend the tree, recording paths not taken + while (!pCursor.IsLeaf) + { + KDNode pNotTaken; + + // If the seach point is larger, select the right path. + if (tSearchPoint[pCursor.iSplitDimension] > pCursor.fSplitValue) + { + pNotTaken = pCursor.pLeft; + pCursor = pCursor.pRight; + } + else + { + pNotTaken = pCursor.pRight; + pCursor = pCursor.pLeft; + } + + // Calculate the shortest distance between the search point and the min and max bounds of the kd-node. + double fDistance = kDistanceFunction.DistanceToRectangle(tSearchPoint, pNotTaken.tMinBound, pNotTaken.tMaxBound); + + // If it is greater than the threshold, skip. + if (fThreshold >= 0 && fDistance > fThreshold) + { + //pPending.Insert(fDistance, pNotTaken); + continue; + } + + // Only add the path we need more points or the node is closer than furthest point on list so far. + if (pEvaluated.Size < iPointsRemaining || fDistance <= pEvaluated.MaxKey) + { + pPending.Insert(fDistance, pNotTaken); + } + } + + // If all the points in this KD node are in one place. + if (pCursor.bSinglePoint) + { + // Work out the distance between this point and the search point. + double fDistance = kDistanceFunction.Distance(pCursor.tPoints[0], tSearchPoint); + + // Skip if the point exceeds the threshold. + // Technically this should never happen, but be prescise. + if (fThreshold >= 0 && fDistance >= fThreshold) + continue; + + // Add the point if either need more points or it's closer than furthest on list so far. + if (pEvaluated.Size < iPointsRemaining || fDistance <= pEvaluated.MaxKey) + { + for (int i = 0; i < pCursor.Size; ++i) + { + // If we don't need any more, replace max + if (pEvaluated.Size == iPointsRemaining) + pEvaluated.ReplaceMax(fDistance, pCursor.tData[i]); + + // Otherwise insert. + else + pEvaluated.Insert(fDistance, pCursor.tData[i]); + } + } + } + + // If the points in the KD node are spread out. + else + { + // Treat the distance of each point seperately. + for (int i = 0; i < pCursor.Size; ++i) + { + // Compute the distance between the points. + double fDistance = kDistanceFunction.Distance(pCursor.tPoints[i], tSearchPoint); + + // Skip if it exceeds the threshold. + if (fThreshold >= 0 && fDistance >= fThreshold) + continue; + + // Insert the point if we have more to take. + if (pEvaluated.Size < iPointsRemaining) + pEvaluated.Insert(fDistance, pCursor.tData[i]); + + // Otherwise replace the max. + else if (fDistance < pEvaluated.MaxKey) + pEvaluated.ReplaceMax(fDistance, pCursor.tData[i]); + } + } + } + + // Select the point with the smallest distance. + if (pEvaluated.Size == 0) + return false; + + iPointsRemaining--; + _CurrentDistance = pEvaluated.MinKey; + _Current = pEvaluated.Min; + pEvaluated.RemoveMin(); + return true; + } + + /// + /// Reset the iterator. + /// + public void Reset() + { + // Store the point count and the distance function. + this.iPointsRemaining = Math.Min(iMaxPointsReturned, pRoot.Size); + _CurrentDistance = -1; + + // Create an interval heap for the points we check. + this.pEvaluated = new IntervalHeap(); + + // Create a min heap for the things we need to check. + this.pPending = new MinHeap>(); + this.pPending.Insert(0, pRoot); + } + + public T Current + { + get { return _Current; } + } + + /// + /// Return the distance of the current value to the search point. + /// + public double CurrentDistance + { + get { return _CurrentDistance; } + } + + /// + /// Return the current value referenced by the iterator as an object. + /// + object IEnumerator.Current + { + get { return _Current; } + } + + /// + /// Return the current value referenced by the iterator. + /// + T IEnumerator.Current + { + get { return _Current; } + } + + public void Dispose() + { + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator GetEnumerator() + { + return this; + } + } +} diff --git a/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/NearestNeighbour.cs.meta b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/NearestNeighbour.cs.meta new file mode 100644 index 000000000..7c0fe0485 --- /dev/null +++ b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTree/NearestNeighbour.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 838799301f7b90c4c9ddad55847c8093 +timeCreated: 1516636092 +licenseType: Pro +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTreeCollection.cs b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTreeCollection.cs new file mode 100644 index 000000000..50e4d3448 --- /dev/null +++ b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTreeCollection.cs @@ -0,0 +1,38 @@ +namespace Mapbox.Unity.MeshGeneration +{ + using UnityEngine; + using KDTree; + using Mapbox.Unity.MeshGeneration.Data; + using System; + + /// + /// KdTree Collection is a feature collection using KdTree mainly for distance based searchs like "find all buildings 100m around + /// player" or "find 10 closest buildings to this point". + /// KdTree structures focus on search performance so querying for features will be very fast using this collection. On the other + /// hand it's not good for dynamic/moving entities but we don't have such features on map so it's one of the best options for maps. + /// + + [CreateAssetMenu(menuName = "Mapbox/Feature Collections/Kd Tree Collection")] + public class KdTreeCollection : FeatureCollectionBase + { + private KDTree _entities; + public int Count; + + public override void Initialize() + { + base.Initialize(); + _entities = new KDTree.KDTree(2); + } + + public override void AddFeature(double[] position, VectorEntity ve) + { + _entities.AddPoint(position, ve); + Count = _entities.Size; + } + + public NearestNeighbour NearestNeighbors(double[] v, int maxCount, float range) + { + return _entities.NearestNeighbors(v, maxCount, range); + } + } +} diff --git a/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTreeCollection.cs.meta b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTreeCollection.cs.meta new file mode 100644 index 000000000..1da05fb37 --- /dev/null +++ b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Data/KdTreeCollection.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 82ed8ed837e25084bbe8a37d53c5b77b +timeCreated: 1519741039 +licenseType: Pro +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Modifiers/GameObjectModifiers/AddToCollectionModifier.cs b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Modifiers/GameObjectModifiers/AddToCollectionModifier.cs new file mode 100644 index 000000000..814d3f2e5 --- /dev/null +++ b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Modifiers/GameObjectModifiers/AddToCollectionModifier.cs @@ -0,0 +1,24 @@ +namespace Mapbox.Unity.MeshGeneration.Modifiers +{ + using UnityEngine; + using Mapbox.Unity.MeshGeneration.Components; + using Mapbox.Unity.MeshGeneration.Data; + + [CreateAssetMenu(menuName = "Mapbox/Modifiers/Add To Collection Modifier")] + public class AddToCollectionModifier : GameObjectModifier + { + [SerializeField] + private FeatureCollectionBase _collection; + + public override void Initialize() + { + base.Initialize(); + _collection.Initialize(); + } + + public override void Run(VectorEntity ve, UnityTile tile) + { + _collection.AddFeature(new double[] { ve.Transform.position.x, ve.Transform.position.z }, ve); + } + } +} diff --git a/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Modifiers/GameObjectModifiers/AddToCollectionModifier.cs.meta b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Modifiers/GameObjectModifiers/AddToCollectionModifier.cs.meta new file mode 100644 index 000000000..287d1430a --- /dev/null +++ b/sdkproject/Assets/Mapbox/Unity/MeshGeneration/Modifiers/GameObjectModifiers/AddToCollectionModifier.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 4538b91da572dfa41adf689573eaba4b +timeCreated: 1519739956 +licenseType: Pro +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: