新增动态水物理插件
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c12907e7fed180445b9b2a55cd5c2a4b
|
||||
folderAsset: yes
|
||||
timeCreated: 1492277071
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,54 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* MIConvexHull, Copyright (c) 2015 David Sehnal, Matthew Campbell
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
namespace NWH.DWP2.MiConvexHull
|
||||
{
|
||||
internal static class Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// The default plane distance tolerance
|
||||
/// </summary>
|
||||
internal const double DefaultPlaneDistanceTolerance = 1e-10;
|
||||
|
||||
/// <summary>
|
||||
/// The starting delta dot product in simplex
|
||||
/// </summary>
|
||||
internal const double StartingDeltaDotProductInSimplex = 0.5;
|
||||
|
||||
/// <summary>
|
||||
/// The connector table size
|
||||
/// </summary>
|
||||
internal const int ConnectorTableSize = 2017;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 532c29ad0c844ee4e8aadc87e89b7aff
|
||||
timeCreated: 1490199237
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,152 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* MIConvexHull, Copyright (c) 2015 David Sehnal, Matthew Campbell
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#region
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.MiConvexHull
|
||||
{
|
||||
/// <summary>
|
||||
/// Factory class for computing convex hulls.
|
||||
/// </summary>
|
||||
public static class ConvexHull
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a convex hull of the input data.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex"> The type of the t vertex. </typeparam>
|
||||
/// <typeparam name="TFace"> The type of the t face. </typeparam>
|
||||
/// <param name="data"> The data. </param>
|
||||
/// <param name="PlaneDistanceTolerance"> The plane distance tolerance. </param>
|
||||
/// <returns>
|
||||
/// ConvexHull<TVertex, TFace>.
|
||||
/// </returns>
|
||||
public static ConvexHull<TVertex, TFace> Create<TVertex, TFace>(IList<TVertex> data,
|
||||
double PlaneDistanceTolerance = Constants.DefaultPlaneDistanceTolerance)
|
||||
where TVertex : IVertex
|
||||
where TFace : ConvexFace<TVertex, TFace>, new()
|
||||
{
|
||||
return ConvexHull<TVertex, TFace>.Create(data, PlaneDistanceTolerance);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a convex hull of the input data.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex"> The type of the t vertex. </typeparam>
|
||||
/// <param name="data"> The data. </param>
|
||||
/// <param name="PlaneDistanceTolerance"> The plane distance tolerance. </param>
|
||||
/// <returns>
|
||||
/// ConvexHull<TVertex, DefaultConvexFace<TVertex>>.
|
||||
/// </returns>
|
||||
public static ConvexHull<TVertex, DefaultConvexFace<TVertex>> Create<TVertex>(IList<TVertex> data,
|
||||
double PlaneDistanceTolerance = Constants.DefaultPlaneDistanceTolerance)
|
||||
where TVertex : IVertex
|
||||
{
|
||||
return ConvexHull<TVertex, DefaultConvexFace<TVertex>>.Create(data, PlaneDistanceTolerance);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a convex hull of the input data.
|
||||
/// </summary>
|
||||
/// <param name="data"> The data. </param>
|
||||
/// <param name="PlaneDistanceTolerance"> The plane distance tolerance. </param>
|
||||
/// <returns>
|
||||
/// ConvexHull<DefaultVertex, DefaultConvexFace<DefaultVertex>>.
|
||||
/// </returns>
|
||||
public static ConvexHull<DefaultVertex, DefaultConvexFace<DefaultVertex>> Create(IList<double[]> data,
|
||||
double PlaneDistanceTolerance = Constants.DefaultPlaneDistanceTolerance)
|
||||
{
|
||||
List<DefaultVertex> points = data.Select(p => new DefaultVertex { Position = p, }).ToList();
|
||||
return ConvexHull<DefaultVertex, DefaultConvexFace<DefaultVertex>>.Create(points, PlaneDistanceTolerance);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Representation of a convex hull.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex"> The type of the t vertex. </typeparam>
|
||||
/// <typeparam name="TFace"> The type of the t face. </typeparam>
|
||||
public class ConvexHull<TVertex, TFace>
|
||||
where TVertex : IVertex
|
||||
where TFace : ConvexFace<TVertex, TFace>, new()
|
||||
{
|
||||
/// <summary>
|
||||
/// Can only be created using a factory method.
|
||||
/// </summary>
|
||||
internal ConvexHull()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Vertices of the convex hull.
|
||||
/// </summary>
|
||||
/// <value> The points. </value>
|
||||
public IEnumerable<TVertex> Points { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Faces of the convex hull.
|
||||
/// </summary>
|
||||
/// <value> The faces. </value>
|
||||
public IEnumerable<TFace> Faces { get; internal set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates the convex hull.
|
||||
/// </summary>
|
||||
/// <param name="data"> The data. </param>
|
||||
/// <param name="PlaneDistanceTolerance"> The plane distance tolerance. </param>
|
||||
/// <returns>
|
||||
/// ConvexHull<TVertex, TFace>.
|
||||
/// </returns>
|
||||
/// <exception cref="System.ArgumentNullException"> The supplied data is null. </exception>
|
||||
/// <exception cref="ArgumentNullException"> data </exception>
|
||||
public static ConvexHull<TVertex, TFace> Create(IList<TVertex> data, double PlaneDistanceTolerance)
|
||||
{
|
||||
if (data == null)
|
||||
{
|
||||
throw new ArgumentNullException("The supplied data is null.");
|
||||
}
|
||||
|
||||
return ConvexHullAlgorithm.GetConvexHull<TVertex, TFace>(data, PlaneDistanceTolerance);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 761f2eca68a8d2c4fbd8f0d07be2153e
|
||||
timeCreated: 1490199238
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f5157676f6e47df4e8abeabb013e44b1
|
||||
folderAsset: yes
|
||||
timeCreated: 1490199232
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,341 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* MIConvexHull, Copyright (c) 2015 David Sehnal, Matthew Campbell
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#region
|
||||
|
||||
using System;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.MiConvexHull
|
||||
{
|
||||
/// <summary>
|
||||
/// A more lightweight alternative to List of T.
|
||||
/// On clear, only resets the count and does not clear the references
|
||||
/// => this works because of the ObjectManager.
|
||||
/// Includes a stack functionality.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"> </typeparam>
|
||||
internal class SimpleList<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// The count
|
||||
/// </summary>
|
||||
public int Count;
|
||||
|
||||
/// <summary>
|
||||
/// The capacity
|
||||
/// </summary>
|
||||
private int capacity;
|
||||
|
||||
/// <summary>
|
||||
/// The items
|
||||
/// </summary>
|
||||
private T[] items;
|
||||
|
||||
/// <summary>
|
||||
/// Get the i-th element.
|
||||
/// </summary>
|
||||
/// <param name="i"> The i. </param>
|
||||
/// <returns> T. </returns>
|
||||
public T this[int i]
|
||||
{
|
||||
get { return items[i]; }
|
||||
set { items[i] = value; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Size matters.
|
||||
/// </summary>
|
||||
private void EnsureCapacity()
|
||||
{
|
||||
if (capacity == 0)
|
||||
{
|
||||
capacity = 32;
|
||||
items = new T[32];
|
||||
}
|
||||
else
|
||||
{
|
||||
T[] newItems = new T[capacity * 2];
|
||||
Array.Copy(items, newItems, capacity);
|
||||
capacity = 2 * capacity;
|
||||
items = newItems;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds a vertex to the buffer.
|
||||
/// </summary>
|
||||
/// <param name="item"> The item. </param>
|
||||
public void Add(T item)
|
||||
{
|
||||
if (Count + 1 > capacity)
|
||||
{
|
||||
EnsureCapacity();
|
||||
}
|
||||
|
||||
items[Count++] = item;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Pushes the value to the back of the list.
|
||||
/// </summary>
|
||||
/// <param name="item"> The item. </param>
|
||||
public void Push(T item)
|
||||
{
|
||||
if (Count + 1 > capacity)
|
||||
{
|
||||
EnsureCapacity();
|
||||
}
|
||||
|
||||
items[Count++] = item;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Pops the last value from the list.
|
||||
/// </summary>
|
||||
/// <returns> T. </returns>
|
||||
public T Pop()
|
||||
{
|
||||
return items[--Count];
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sets the Count to 0, otherwise does nothing.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
Count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A fancy name for a list of integers.
|
||||
/// </summary>
|
||||
/// <seealso cref="NWH.DWP2.MiConvexHull.SimpleList{System.Int32}" />
|
||||
internal class IndexBuffer : SimpleList<int>
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A priority based linked list.
|
||||
/// </summary>
|
||||
internal sealed class FaceList
|
||||
{
|
||||
/// <summary>
|
||||
/// The last
|
||||
/// </summary>
|
||||
private ConvexFaceInternal last;
|
||||
|
||||
/// <summary>
|
||||
/// Get the first element.
|
||||
/// </summary>
|
||||
/// <value> The first. </value>
|
||||
public ConvexFaceInternal First { get; private set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds the element to the beginning.
|
||||
/// </summary>
|
||||
/// <param name="face"> The face. </param>
|
||||
private void AddFirst(ConvexFaceInternal face)
|
||||
{
|
||||
face.InList = true;
|
||||
First.Previous = face;
|
||||
face.Next = First;
|
||||
First = face;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds a face to the list.
|
||||
/// </summary>
|
||||
/// <param name="face"> The face. </param>
|
||||
public void Add(ConvexFaceInternal face)
|
||||
{
|
||||
if (face.InList)
|
||||
{
|
||||
if (First.VerticesBeyond.Count < face.VerticesBeyond.Count)
|
||||
{
|
||||
Remove(face);
|
||||
AddFirst(face);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
face.InList = true;
|
||||
|
||||
if (First != null && First.VerticesBeyond.Count < face.VerticesBeyond.Count)
|
||||
{
|
||||
First.Previous = face;
|
||||
face.Next = First;
|
||||
First = face;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (last != null)
|
||||
{
|
||||
last.Next = face;
|
||||
}
|
||||
|
||||
face.Previous = last;
|
||||
last = face;
|
||||
if (First == null)
|
||||
{
|
||||
First = face;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Removes the element from the list.
|
||||
/// </summary>
|
||||
/// <param name="face"> The face. </param>
|
||||
public void Remove(ConvexFaceInternal face)
|
||||
{
|
||||
if (!face.InList)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
face.InList = false;
|
||||
|
||||
if (face.Previous != null)
|
||||
{
|
||||
face.Previous.Next = face.Next;
|
||||
}
|
||||
else if ( /*first == face*/ face.Previous == null)
|
||||
{
|
||||
First = face.Next;
|
||||
}
|
||||
|
||||
if (face.Next != null)
|
||||
{
|
||||
face.Next.Previous = face.Previous;
|
||||
}
|
||||
else if ( /*last == face*/ face.Next == null)
|
||||
{
|
||||
last = face.Previous;
|
||||
}
|
||||
|
||||
face.Next = null;
|
||||
face.Previous = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connector list.
|
||||
/// </summary>
|
||||
internal sealed class ConnectorList
|
||||
{
|
||||
/// <summary>
|
||||
/// The last
|
||||
/// </summary>
|
||||
private FaceConnector last;
|
||||
|
||||
/// <summary>
|
||||
/// Get the first element.
|
||||
/// </summary>
|
||||
/// <value> The first. </value>
|
||||
public FaceConnector First { get; private set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds the element to the beginning.
|
||||
/// </summary>
|
||||
/// <param name="connector"> The connector. </param>
|
||||
private void AddFirst(FaceConnector connector)
|
||||
{
|
||||
First.Previous = connector;
|
||||
connector.Next = First;
|
||||
First = connector;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds a face to the list.
|
||||
/// </summary>
|
||||
/// <param name="element"> The element. </param>
|
||||
public void Add(FaceConnector element)
|
||||
{
|
||||
if (last != null)
|
||||
{
|
||||
last.Next = element;
|
||||
}
|
||||
|
||||
element.Previous = last;
|
||||
last = element;
|
||||
if (First == null)
|
||||
{
|
||||
First = element;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Removes the element from the list.
|
||||
/// </summary>
|
||||
/// <param name="connector"> The connector. </param>
|
||||
public void Remove(FaceConnector connector)
|
||||
{
|
||||
if (connector.Previous != null)
|
||||
{
|
||||
connector.Previous.Next = connector.Next;
|
||||
}
|
||||
else if ( /*first == face*/ connector.Previous == null)
|
||||
{
|
||||
First = connector.Next;
|
||||
}
|
||||
|
||||
if (connector.Next != null)
|
||||
{
|
||||
connector.Next.Previous = connector.Previous;
|
||||
}
|
||||
else if ( /*last == face*/ connector.Next == null)
|
||||
{
|
||||
last = connector.Previous;
|
||||
}
|
||||
|
||||
connector.Next = null;
|
||||
connector.Previous = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 290297a1af25922458e84d0d4d3748ac
|
||||
timeCreated: 1490199237
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,77 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* MIConvexHull, Copyright (c) 2015 David Sehnal, Matthew Campbell
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
namespace NWH.DWP2.MiConvexHull
|
||||
{
|
||||
/// <summary>
|
||||
/// A convex face representation containing adjacency information.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex"> The type of the t vertex. </typeparam>
|
||||
/// <typeparam name="TFace"> The type of the t face. </typeparam>
|
||||
public abstract class ConvexFace<TVertex, TFace>
|
||||
where TVertex : IVertex
|
||||
where TFace : ConvexFace<TVertex, TFace>
|
||||
{
|
||||
/// <summary>
|
||||
/// Adjacency. Array of length "dimension".
|
||||
/// If F = Adjacency[i] then the vertices shared with F are Vertices[j] where j != i.
|
||||
/// In the context of triangulation, can be null (indicates the cell is at boundary).
|
||||
/// </summary>
|
||||
/// <value> The adjacency. </value>
|
||||
public TFace[] Adjacency { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The vertices stored in clockwise order for dimensions 2 - 4, in higher dimensions the order is arbitrary.
|
||||
/// Unless I accidentally switch some index somewhere in which case the order is CCW. Either way, it is consistent.
|
||||
/// 3D Normal = (V[1] - V[0]) x (V[2] - V[1]).
|
||||
/// </summary>
|
||||
/// <value> The vertices. </value>
|
||||
public TVertex[] Vertices { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The normal vector of the face. Null if used in triangulation.
|
||||
/// </summary>
|
||||
/// <value> The normal. </value>
|
||||
public double[] Normal { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A default convex face representation that inherits from ConvexFace with self-referencing type parameter.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex"> The type of the vertex. </typeparam>
|
||||
public class DefaultConvexFace<TVertex> : ConvexFace<TVertex, DefaultConvexFace<TVertex>>
|
||||
where TVertex : IVertex
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f4d8d869c3901dd40b08d3ae58f3ace3
|
||||
timeCreated: 1490199238
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,845 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* MIConvexHull, Copyright (c) 2015 David Sehnal, Matthew Campbell
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#region
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.MiConvexHull
|
||||
{
|
||||
/*
|
||||
* This part of the implementation handles initialization of the convex hull algorithm:
|
||||
* - Constructor & Process initiation
|
||||
* - Determine the dimension by looking at length of Position vector of 10 random data points from the input.
|
||||
* - Identify bounding box points in each direction.
|
||||
* - Pick (Dimension + 1) points from the extremes and construct the initial simplex.
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Class ConvexHullAlgorithm.
|
||||
/// </summary>
|
||||
internal partial class ConvexHullAlgorithm
|
||||
{
|
||||
/// <summary>
|
||||
/// Serializes the vertices into the 1D array, Positions. The 1D array has much quicker access in C#.
|
||||
/// </summary>
|
||||
private void SerializeVerticesToPositions()
|
||||
{
|
||||
int index = 0;
|
||||
if (IsLifted) // "Lifted" means that the last dimension is the sum of the squares of the others.
|
||||
{
|
||||
foreach (IVertex v in Vertices)
|
||||
{
|
||||
double parabolaTerm = 0.0; // the lifted term is a sum of squares.
|
||||
int origNumDim = NumOfDimensions - 1;
|
||||
for (int i = 0; i < origNumDim; i++)
|
||||
{
|
||||
double coordinate = v.Position[i];
|
||||
Positions[index++] = coordinate;
|
||||
parabolaTerm += coordinate * coordinate;
|
||||
}
|
||||
|
||||
Positions[index++] = parabolaTerm;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (IVertex v in Vertices)
|
||||
{
|
||||
for (int i = 0; i < NumOfDimensions; i++)
|
||||
{
|
||||
Positions[index++] = v.Position[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Finds the bounding box points.
|
||||
/// </summary>
|
||||
private void FindBoundingBoxPoints()
|
||||
{
|
||||
indexOfDimensionWithLeastExtremes = -1;
|
||||
int minNumExtremes = int.MaxValue;
|
||||
for (int i = 0; i < NumOfDimensions; i++)
|
||||
{
|
||||
List<int> minIndices = new();
|
||||
List<int> maxIndices = new();
|
||||
double min = double.PositiveInfinity, max = double.NegativeInfinity;
|
||||
for (int j = 0; j < NumberOfVertices; j++)
|
||||
{
|
||||
double v = GetCoordinate(j, i);
|
||||
double difference = min - v;
|
||||
if (difference >= PlaneDistanceTolerance)
|
||||
{
|
||||
// you found a better solution than before, clear out the list and store new value
|
||||
min = v;
|
||||
minIndices.Clear();
|
||||
minIndices.Add(j);
|
||||
}
|
||||
else if (difference > 0)
|
||||
{
|
||||
// you found a solution slightly better than before, clear out those that are no longer on the
|
||||
// list and store new value
|
||||
min = v;
|
||||
minIndices.RemoveAll(index => min - GetCoordinate(index, i) > PlaneDistanceTolerance);
|
||||
minIndices.Add(j);
|
||||
}
|
||||
else if (difference > -PlaneDistanceTolerance)
|
||||
{
|
||||
//same or almost as good as current limit, so store it
|
||||
minIndices.Add(j);
|
||||
}
|
||||
|
||||
difference = v - max;
|
||||
if (difference >= PlaneDistanceTolerance)
|
||||
{
|
||||
// you found a better solution than before, clear out the list and store new value
|
||||
max = v;
|
||||
maxIndices.Clear();
|
||||
maxIndices.Add(j);
|
||||
}
|
||||
else if (difference > 0)
|
||||
{
|
||||
// you found a solution slightly better than before, clear out those that are no longer on the
|
||||
// list and store new value
|
||||
max = v;
|
||||
maxIndices.RemoveAll(index => min - GetCoordinate(index, i) > PlaneDistanceTolerance);
|
||||
maxIndices.Add(j);
|
||||
}
|
||||
else if (difference > -PlaneDistanceTolerance)
|
||||
{
|
||||
//same or almost as good as current limit, so store it
|
||||
maxIndices.Add(j);
|
||||
}
|
||||
}
|
||||
|
||||
minima[i] = min;
|
||||
maxima[i] = max;
|
||||
minIndices.AddRange(maxIndices);
|
||||
if (minIndices.Count < minNumExtremes)
|
||||
{
|
||||
minNumExtremes = minIndices.Count;
|
||||
indexOfDimensionWithLeastExtremes = i;
|
||||
}
|
||||
|
||||
boundingBoxPoints[i] = minIndices;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Shifts and scales the Positions to avoid future errors. This does not alter the original data.
|
||||
/// </summary>
|
||||
private void ShiftAndScalePositions()
|
||||
{
|
||||
int positionsLength = Positions.Length;
|
||||
if (IsLifted)
|
||||
{
|
||||
int origNumDim = NumOfDimensions - 1;
|
||||
double parabolaScale = 2 / (minima.Sum(x => Math.Abs(x)) + maxima.Sum(x => Math.Abs(x))
|
||||
- Math.Abs(maxima[origNumDim]) - Math.Abs(minima[origNumDim]));
|
||||
// the parabolascale is 1 / average of the sum of the other dimensions.
|
||||
// multiplying this by the parabola will scale it back to be on near similar size to the
|
||||
// other dimensions. Without this, the term is much larger than the others, which causes
|
||||
// problems for roundoff error and finding the normal of faces.
|
||||
minima[origNumDim] *= parabolaScale; // change the extreme values as well
|
||||
maxima[origNumDim] *= parabolaScale;
|
||||
// it is done here because
|
||||
for (int i = origNumDim; i < positionsLength; i += NumOfDimensions)
|
||||
{
|
||||
Positions[i] *= parabolaScale;
|
||||
}
|
||||
}
|
||||
|
||||
double[] shiftAmount = new double[NumOfDimensions];
|
||||
for (int i = 0; i < NumOfDimensions; i++)
|
||||
// now the entire model is shifted to all positive numbers...plus some more.
|
||||
// why?
|
||||
// 1) to avoid dealing with a point at the origin {0,0,...,0} which causes problems
|
||||
// for future normal finding
|
||||
// 2) note that wierd shift that is used (max - min - min). This is to avoid scaling
|
||||
// issues. this shift means that the minima in a dimension will always be a positive
|
||||
// number (no points at zero), and the minima [in a given dimension] will always be
|
||||
// half of the maxima. 'Half' is much preferred to 'thousands of times'
|
||||
// Think of the first term as the range (max - min), then the second term avoids cases
|
||||
// where there are both positive and negative numbers.
|
||||
{
|
||||
shiftAmount[i] = maxima[i] - minima[i] - minima[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < positionsLength; i++)
|
||||
{
|
||||
Positions[i] += shiftAmount[i % NumOfDimensions];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Find the (dimension+1) initial points and create the simplexes.
|
||||
/// Creates the initial simplex of n+1 vertices by using points from the bounding box.
|
||||
/// Special care is taken to ensure that the vertices chosen do not result in a degenerate shape
|
||||
/// where vertices are collinear (co-planar, etc). This would technically be resolved when additional
|
||||
/// vertices are checked in the main loop, but: 1) a degenerate simplex would not eliminate any other
|
||||
/// vertices (thus no savings there), 2) the creation of the face normal is prone to error.
|
||||
/// </summary>
|
||||
private void CreateInitialSimplex()
|
||||
{
|
||||
List<int> initialPoints = FindInitialPoints();
|
||||
|
||||
#region Create the first faces from (dimension + 1) vertices.
|
||||
|
||||
int[] faces = new int[NumOfDimensions + 1];
|
||||
|
||||
for (int i = 0; i < NumOfDimensions + 1; i++)
|
||||
{
|
||||
int[] vertices = new int[NumOfDimensions];
|
||||
for (int j = 0, k = 0; j <= NumOfDimensions; j++)
|
||||
{
|
||||
if (i != j)
|
||||
{
|
||||
vertices[k++] = initialPoints[j];
|
||||
}
|
||||
}
|
||||
|
||||
ConvexFaceInternal newFace = FacePool[ObjectManager.GetFace()];
|
||||
newFace.Vertices = vertices;
|
||||
Array.Sort(vertices);
|
||||
MathHelper.CalculateFacePlane(newFace, Center);
|
||||
faces[i] = newFace.Index;
|
||||
}
|
||||
|
||||
// update the adjacency (check all pairs of faces)
|
||||
for (int i = 0; i < NumOfDimensions; i++)
|
||||
for (int j = i + 1; j < NumOfDimensions + 1; j++)
|
||||
{
|
||||
UpdateAdjacency(FacePool[faces[i]], FacePool[faces[j]]);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Init the vertex beyond buffers.
|
||||
|
||||
foreach (int faceIndex in faces)
|
||||
{
|
||||
ConvexFaceInternal face = FacePool[faceIndex];
|
||||
FindBeyondVertices(face);
|
||||
if (face.VerticesBeyond.Count == 0)
|
||||
{
|
||||
ConvexFaces.Add(face.Index); // The face is on the hull
|
||||
}
|
||||
else
|
||||
{
|
||||
UnprocessedFaces.Add(face);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// Set all vertices to false (unvisited).
|
||||
foreach (int vertex in initialPoints)
|
||||
{
|
||||
VertexVisited[vertex] = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Finds (dimension + 1) initial points.
|
||||
/// </summary>
|
||||
/// <param name="extremes"> </param>
|
||||
/// <returns> </returns>
|
||||
private List<int> FindInitialPoints()
|
||||
{
|
||||
double bigNumber = maxima.Sum() * NumOfDimensions * NumberOfVertices;
|
||||
// the first two points are taken from the dimension that had the fewest extremes
|
||||
// well, in most cases there will only be 2 in all dimensions: one min and one max
|
||||
// but a lot of engineering part shapes are nice and square and can have hundreds of
|
||||
// parallel vertices at the extremes
|
||||
int vertex1 =
|
||||
boundingBoxPoints[indexOfDimensionWithLeastExtremes].First(); // these are min and max vertices along
|
||||
int vertex2 =
|
||||
boundingBoxPoints[indexOfDimensionWithLeastExtremes].Last(); // the dimension that had the fewest points
|
||||
boundingBoxPoints[indexOfDimensionWithLeastExtremes].RemoveAt(0);
|
||||
boundingBoxPoints[indexOfDimensionWithLeastExtremes]
|
||||
.RemoveAt(boundingBoxPoints[indexOfDimensionWithLeastExtremes].Count - 1);
|
||||
List<int> initialPoints = new() { vertex1, vertex2, };
|
||||
VertexVisited[vertex1] = VertexVisited[vertex2] = true;
|
||||
CurrentVertex = vertex1;
|
||||
UpdateCenter();
|
||||
CurrentVertex = vertex2;
|
||||
UpdateCenter();
|
||||
double[][] edgeVectors = new double[NumOfDimensions][];
|
||||
edgeVectors[0] = MathHelper.VectorBetweenVertices(vertex2, vertex1);
|
||||
// now the remaining vertices are just combined in one big list
|
||||
List<int> extremes = boundingBoxPoints.SelectMany(x => x).ToList();
|
||||
// otherwise find the remaining points by maximizing the initial simplex volume
|
||||
int index = 1;
|
||||
while (index < NumOfDimensions && extremes.Any())
|
||||
{
|
||||
int bestVertex = -1;
|
||||
double[] bestEdgeVector = { };
|
||||
double maxVolume = 0.0;
|
||||
for (int i = extremes.Count - 1; i >= 0; i--)
|
||||
{
|
||||
// count backwards in order to remove potential duplicates
|
||||
int vIndex = extremes[i];
|
||||
if (initialPoints.Contains(vIndex))
|
||||
{
|
||||
extremes.RemoveAt(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
edgeVectors[index] = MathHelper.VectorBetweenVertices(vIndex, vertex1);
|
||||
double volume = MathHelper.GetSimplexVolume(edgeVectors, index, bigNumber);
|
||||
if (maxVolume < volume)
|
||||
{
|
||||
maxVolume = volume;
|
||||
bestVertex = vIndex;
|
||||
bestEdgeVector = edgeVectors[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extremes.Remove(bestVertex);
|
||||
if (bestVertex == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
initialPoints.Add(bestVertex);
|
||||
edgeVectors[index++] = bestEdgeVector;
|
||||
CurrentVertex = bestVertex;
|
||||
UpdateCenter();
|
||||
}
|
||||
|
||||
// hmm, there are not enough points on the bounding box to make a simplex. It is rare but entirely possibly.
|
||||
// As an extreme, the bounding box can be made in n dimensions from only 2 unique points. When we can't find
|
||||
// enough unique points, we start again with ALL the vertices. The following is a near replica of the code
|
||||
// above, but instead of extremes, we consider "allVertices".
|
||||
if (initialPoints.Count <= NumOfDimensions)
|
||||
{
|
||||
List<int> allVertices = Enumerable.Range(0, NumberOfVertices).ToList();
|
||||
while (index < NumOfDimensions && allVertices.Any())
|
||||
{
|
||||
int bestVertex = -1;
|
||||
double[] bestEdgeVector = { };
|
||||
double maxVolume = 0.0;
|
||||
for (int i = allVertices.Count - 1; i >= 0; i--)
|
||||
{
|
||||
// count backwards in order to remove potential duplicates
|
||||
int vIndex = allVertices[i];
|
||||
if (initialPoints.Contains(vIndex))
|
||||
{
|
||||
allVertices.RemoveAt(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
edgeVectors[index] = MathHelper.VectorBetweenVertices(vIndex, vertex1);
|
||||
double volume = MathHelper.GetSimplexVolume(edgeVectors, index, bigNumber);
|
||||
if (maxVolume < volume)
|
||||
{
|
||||
maxVolume = volume;
|
||||
bestVertex = vIndex;
|
||||
bestEdgeVector = edgeVectors[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
allVertices.Remove(bestVertex);
|
||||
if (bestVertex == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
initialPoints.Add(bestVertex);
|
||||
edgeVectors[index++] = bestEdgeVector;
|
||||
CurrentVertex = bestVertex;
|
||||
UpdateCenter();
|
||||
}
|
||||
}
|
||||
|
||||
if (initialPoints.Count <= NumOfDimensions)
|
||||
{
|
||||
throw new ArgumentException("The input data is degenerate. It appears to exist in " + NumOfDimensions +
|
||||
" dimensions, but it is a " + (NumOfDimensions - 1) +
|
||||
" dimensional set (i.e. the point of collinear,"
|
||||
+ " coplanar, or co-hyperplanar.)");
|
||||
}
|
||||
|
||||
return initialPoints;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Check if 2 faces are adjacent and if so, update their AdjacentFaces array.
|
||||
/// </summary>
|
||||
/// <param name="l"> The l. </param>
|
||||
/// <param name="r"> The r. </param>
|
||||
private void UpdateAdjacency(ConvexFaceInternal l, ConvexFaceInternal r)
|
||||
{
|
||||
int[] lv = l.Vertices;
|
||||
int[] rv = r.Vertices;
|
||||
int i;
|
||||
|
||||
// reset marks on the 1st face
|
||||
for (i = 0; i < lv.Length; i++)
|
||||
{
|
||||
VertexVisited[lv[i]] = false;
|
||||
}
|
||||
|
||||
// mark all vertices on the 2nd face
|
||||
for (i = 0; i < rv.Length; i++)
|
||||
{
|
||||
VertexVisited[rv[i]] = true;
|
||||
}
|
||||
|
||||
// find the 1st false index
|
||||
for (i = 0; i < lv.Length; i++)
|
||||
{
|
||||
if (!VertexVisited[lv[i]])
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// no vertex was marked
|
||||
if (i == NumOfDimensions)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// check if only 1 vertex wasn't marked
|
||||
for (int j = i + 1; j < lv.Length; j++)
|
||||
{
|
||||
if (!VertexVisited[lv[j]])
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if we are here, the two faces share an edge
|
||||
l.AdjacentFaces[i] = r.Index;
|
||||
|
||||
// update the adj. face on the other face - find the vertex that remains marked
|
||||
for (i = 0; i < lv.Length; i++)
|
||||
{
|
||||
VertexVisited[lv[i]] = false;
|
||||
}
|
||||
|
||||
for (i = 0; i < rv.Length; i++)
|
||||
{
|
||||
if (VertexVisited[rv[i]])
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
r.AdjacentFaces[i] = l.Index;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Used in the "initialization" code.
|
||||
/// </summary>
|
||||
/// <param name="face"> The face. </param>
|
||||
private void FindBeyondVertices(ConvexFaceInternal face)
|
||||
{
|
||||
IndexBuffer beyondVertices = face.VerticesBeyond;
|
||||
MaxDistance = double.NegativeInfinity;
|
||||
FurthestVertex = 0;
|
||||
for (int i = 0; i < NumberOfVertices; i++)
|
||||
{
|
||||
if (VertexVisited[i])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
IsBeyond(face, beyondVertices, i);
|
||||
}
|
||||
|
||||
face.FurthestVertex = FurthestVertex;
|
||||
}
|
||||
|
||||
|
||||
#region Starting functions and constructor
|
||||
|
||||
/// <summary>
|
||||
/// The main function for the Convex Hull algorithm. It is static, but it creates
|
||||
/// an instantiation of this class in order to allow for parallel execution.
|
||||
/// Following this simple function, the constructor and the main function "FindConvexHull" is listed.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex"> The type of the vertices in the data. </typeparam>
|
||||
/// <typeparam name="TFace"> The desired type of the faces. </typeparam>
|
||||
/// <param name="data"> The data is the vertices as a collection of IVertices. </param>
|
||||
/// <param name="PlaneDistanceTolerance"> The plane distance tolerance. </param>
|
||||
/// <returns>
|
||||
/// MIConvexHull.ConvexHull<TVertex, TFace>.
|
||||
/// </returns>
|
||||
internal static ConvexHull<TVertex, TFace> GetConvexHull<TVertex, TFace>(IList<TVertex> data,
|
||||
double PlaneDistanceTolerance)
|
||||
where TFace : ConvexFace<TVertex, TFace>, new()
|
||||
where TVertex : IVertex
|
||||
{
|
||||
ConvexHullAlgorithm ch = new(data.Cast<IVertex>().ToArray(), false, PlaneDistanceTolerance);
|
||||
ch.GetConvexHull();
|
||||
|
||||
return new ConvexHull<TVertex, TFace>
|
||||
{
|
||||
Points = ch.GetHullVertices(data),
|
||||
Faces = ch.GetConvexFaces<TVertex, TFace>(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConvexHullAlgorithm" /> class.
|
||||
/// </summary>
|
||||
/// <param name="vertices"> The vertices. </param>
|
||||
/// <param name="lift"> if set to <c> true </c> [lift]. </param>
|
||||
/// <param name="PlaneDistanceTolerance"> The plane distance tolerance. </param>
|
||||
/// <exception cref="System.InvalidOperationException"> Dimension of the input must be 2 or greater. </exception>
|
||||
/// <exception cref="System.ArgumentException">
|
||||
/// There are too few vertices (m) for the n-dimensional space. (m must be greater +
|
||||
/// than the n, but m is + NumberOfVertices + and n is + NumOfDimensions
|
||||
/// </exception>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// PointTranslationGenerator cannot be null if PointTranslationType is enabled.
|
||||
/// or
|
||||
/// Dimension of the input must be 2 or greater.
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// There are too few vertices (m) for the n-dimensional space. (m must be greater " +
|
||||
/// "than the n, but m is " + NumberOfVertices + " and n is " + Dimension
|
||||
/// </exception>
|
||||
private ConvexHullAlgorithm(IVertex[] vertices, bool lift, double PlaneDistanceTolerance)
|
||||
{
|
||||
IsLifted = lift;
|
||||
Vertices = vertices;
|
||||
NumberOfVertices = vertices.Length;
|
||||
|
||||
NumOfDimensions = DetermineDimension();
|
||||
if (IsLifted)
|
||||
{
|
||||
NumOfDimensions++;
|
||||
}
|
||||
|
||||
if (NumOfDimensions < 2)
|
||||
{
|
||||
throw new InvalidOperationException("Dimension of the input must be 2 or greater.");
|
||||
}
|
||||
|
||||
if (NumberOfVertices <= NumOfDimensions)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"There are too few vertices (m) for the n-dimensional space. (m must be greater " +
|
||||
"than the n, but m is " + NumberOfVertices + " and n is " + NumOfDimensions);
|
||||
}
|
||||
|
||||
this.PlaneDistanceTolerance = PlaneDistanceTolerance;
|
||||
UnprocessedFaces = new FaceList();
|
||||
ConvexFaces = new IndexBuffer();
|
||||
|
||||
FacePool = new ConvexFaceInternal[(NumOfDimensions + 1) * 10]; // must be initialized before object manager
|
||||
AffectedFaceFlags = new bool[(NumOfDimensions + 1) * 10];
|
||||
ObjectManager = new ObjectManager(this);
|
||||
|
||||
Center = new double[NumOfDimensions];
|
||||
TraverseStack = new IndexBuffer();
|
||||
UpdateBuffer = new int[NumOfDimensions];
|
||||
UpdateIndices = new int[NumOfDimensions];
|
||||
EmptyBuffer = new IndexBuffer();
|
||||
AffectedFaceBuffer = new IndexBuffer();
|
||||
ConeFaceBuffer = new SimpleList<DeferredFace>();
|
||||
SingularVertices = new HashSet<int>();
|
||||
BeyondBuffer = new IndexBuffer();
|
||||
|
||||
ConnectorTable = new ConnectorList[Constants.ConnectorTableSize];
|
||||
for (int i = 0; i < Constants.ConnectorTableSize; i++)
|
||||
{
|
||||
ConnectorTable[i] = new ConnectorList();
|
||||
}
|
||||
|
||||
VertexVisited = new bool[NumberOfVertices];
|
||||
Positions = new double[NumberOfVertices * NumOfDimensions];
|
||||
boundingBoxPoints = new List<int>[NumOfDimensions];
|
||||
minima = new double[NumOfDimensions];
|
||||
maxima = new double[NumOfDimensions];
|
||||
MathHelper = new MathHelper(NumOfDimensions, Positions);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Check the dimensionality of the input data.
|
||||
/// </summary>
|
||||
/// <returns> System.Int32. </returns>
|
||||
/// <exception cref="ArgumentException"> Invalid input data (non-uniform dimension). </exception>
|
||||
private int DetermineDimension()
|
||||
{
|
||||
Random r = new();
|
||||
List<int> dimensions = new();
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
dimensions.Add(Vertices[r.Next(NumberOfVertices)].Position.Length);
|
||||
}
|
||||
|
||||
int dimension = dimensions.Min();
|
||||
if (dimension != dimensions.Max())
|
||||
{
|
||||
throw new ArgumentException("Invalid input data (non-uniform dimension).");
|
||||
}
|
||||
|
||||
return dimension;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets/calculates the convex hull. This is
|
||||
/// </summary>
|
||||
private void GetConvexHull()
|
||||
{
|
||||
// accessing a 1D array is quicker than a jagged array, so the first step is to make this array
|
||||
SerializeVerticesToPositions();
|
||||
// next the bounding box extremes are found. This is used to shift, scale and find the starting simplex.
|
||||
FindBoundingBoxPoints();
|
||||
// the positions are shifted to avoid divide by zero problems
|
||||
// and if Delaunay or Voronoi, then the parabola terms are scaled back to match the size of the other coords
|
||||
ShiftAndScalePositions();
|
||||
// Find the (dimension+1) initial points and create the simplexes.
|
||||
CreateInitialSimplex();
|
||||
|
||||
// Now, the main loop. These initial faces of a simplex are replaced and expanded
|
||||
// outwards to make the convex hull and faces.
|
||||
while (UnprocessedFaces.First != null)
|
||||
{
|
||||
ConvexFaceInternal currentFace = UnprocessedFaces.First;
|
||||
CurrentVertex = currentFace.FurthestVertex;
|
||||
|
||||
UpdateCenter();
|
||||
|
||||
// The affected faces get tagged
|
||||
TagAffectedFaces(currentFace);
|
||||
|
||||
// Create the cone from the currentVertex and the affected faces horizon.
|
||||
if (!SingularVertices.Contains(CurrentVertex) && CreateCone())
|
||||
{
|
||||
CommitCone();
|
||||
}
|
||||
else
|
||||
{
|
||||
HandleSingular();
|
||||
}
|
||||
|
||||
// Need to reset the tags
|
||||
int count = AffectedFaceBuffer.Count;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
AffectedFaceFlags[AffectedFaceBuffer[i]] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// Corresponds to the dimension of the data.
|
||||
/// When the "lifted" hull is computed, Dimension is automatically incremented by one.
|
||||
/// </summary>
|
||||
internal readonly int NumOfDimensions;
|
||||
|
||||
/// <summary>
|
||||
/// Are we on a paraboloid?
|
||||
/// </summary>
|
||||
private readonly bool IsLifted;
|
||||
|
||||
/// <summary>
|
||||
/// Explained in ConvexHullComputationConfig.
|
||||
/// </summary>
|
||||
private readonly double PlaneDistanceTolerance;
|
||||
|
||||
/*
|
||||
* Representation of the input vertices.
|
||||
*
|
||||
* - In the algorithm, a vertex is represented by its index in the Vertices array.
|
||||
* This makes the algorithm a lot faster (up to 30%) than using object reference everywhere.
|
||||
* - Positions are stored as a single array of values. Coordinates for vertex with index i
|
||||
* are stored at indices <i * Dimension, (i + 1) * Dimension)
|
||||
* - VertexMarks are used by the algorithm to help identify a set of vertices that is "above" (or "beyond")
|
||||
* a specific face.
|
||||
*/
|
||||
/// <summary>
|
||||
/// The vertices
|
||||
/// </summary>
|
||||
private readonly IVertex[] Vertices;
|
||||
|
||||
/// <summary>
|
||||
/// The positions
|
||||
/// </summary>
|
||||
private readonly double[] Positions;
|
||||
|
||||
/// <summary>
|
||||
/// The vertex marks
|
||||
/// </summary>
|
||||
private readonly bool[] VertexVisited;
|
||||
|
||||
private readonly int NumberOfVertices;
|
||||
|
||||
/*
|
||||
* The triangulation faces are represented in a single pool for objects that are being reused.
|
||||
* This allows for represent the faces as integers and significantly speeds up many computations.
|
||||
* - AffectedFaceFlags are used to mark affected faces/
|
||||
*/
|
||||
/// <summary>
|
||||
/// The face pool
|
||||
/// </summary>
|
||||
internal ConvexFaceInternal[] FacePool;
|
||||
|
||||
/// <summary>
|
||||
/// The affected face flags
|
||||
/// </summary>
|
||||
internal bool[] AffectedFaceFlags;
|
||||
|
||||
/// <summary>
|
||||
/// Used to track the size of the current hull in the Update/RollbackCenter functions.
|
||||
/// </summary>
|
||||
private int ConvexHullSize;
|
||||
|
||||
/// <summary>
|
||||
/// A list of faces that that are not a part of the final convex hull and still need to be processed.
|
||||
/// </summary>
|
||||
private readonly FaceList UnprocessedFaces;
|
||||
|
||||
/// <summary>
|
||||
/// A list of faces that form the convex hull.
|
||||
/// </summary>
|
||||
private readonly IndexBuffer ConvexFaces;
|
||||
|
||||
/// <summary>
|
||||
/// The vertex that is currently being processed.
|
||||
/// </summary>
|
||||
private int CurrentVertex;
|
||||
|
||||
/// <summary>
|
||||
/// A helper variable to determine the furthest vertex for a particular convex face.
|
||||
/// </summary>
|
||||
private double MaxDistance;
|
||||
|
||||
/// <summary>
|
||||
/// A helper variable to help determine the index of the vertex that is furthest from the face that is currently being
|
||||
/// processed.
|
||||
/// </summary>
|
||||
private int FurthestVertex;
|
||||
|
||||
/// <summary>
|
||||
/// The centroid of the currently computed hull.
|
||||
/// </summary>
|
||||
private readonly double[] Center;
|
||||
|
||||
/*
|
||||
* Helper arrays to store faces for adjacency update.
|
||||
* This is just to prevent unnecessary allocations.
|
||||
*/
|
||||
/// <summary>
|
||||
/// The update buffer
|
||||
/// </summary>
|
||||
private readonly int[] UpdateBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// The update indices
|
||||
/// </summary>
|
||||
private readonly int[] UpdateIndices;
|
||||
|
||||
/// <summary>
|
||||
/// Used to determine which faces need to be updated at each step of the algorithm.
|
||||
/// </summary>
|
||||
private readonly IndexBuffer TraverseStack;
|
||||
|
||||
/// <summary>
|
||||
/// Used for VerticesBeyond for faces that are on the convex hull.
|
||||
/// </summary>
|
||||
private readonly IndexBuffer EmptyBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// Used to determine which vertices are "above" (or "beyond") a face
|
||||
/// </summary>
|
||||
private IndexBuffer BeyondBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// Stores faces that are visible from the current vertex.
|
||||
/// </summary>
|
||||
private readonly IndexBuffer AffectedFaceBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// Stores faces that form a "cone" created by adding new vertex.
|
||||
/// </summary>
|
||||
private readonly SimpleList<DeferredFace> ConeFaceBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// Stores a list of "singular" (or "generate", "planar", etc.) vertices that cannot be part of the hull.
|
||||
/// </summary>
|
||||
private readonly HashSet<int> SingularVertices;
|
||||
|
||||
/// <summary>
|
||||
/// The connector table helps to determine the adjacency of convex faces.
|
||||
/// Hashing is used instead of pairwise comparison. This significantly speeds up the computations,
|
||||
/// especially for higher dimensions.
|
||||
/// </summary>
|
||||
private readonly ConnectorList[] ConnectorTable;
|
||||
|
||||
/// <summary>
|
||||
/// Manages the memory allocations and storage of unused objects.
|
||||
/// Saves the garbage collector a lot of work.
|
||||
/// </summary>
|
||||
private readonly ObjectManager ObjectManager;
|
||||
|
||||
/// <summary>
|
||||
/// Helper class for handling math related stuff.
|
||||
/// </summary>
|
||||
private readonly MathHelper MathHelper;
|
||||
|
||||
private readonly List<int>[] boundingBoxPoints;
|
||||
private int indexOfDimensionWithLeastExtremes;
|
||||
private readonly double[] minima;
|
||||
private readonly double[] maxima;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b06a7aa17755adf42828257767d0271a
|
||||
timeCreated: 1490199238
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,683 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* MIConvexHull, Copyright (c) 2015 David Sehnal, Matthew Campbell
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#region
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.MiConvexHull
|
||||
{
|
||||
/*
|
||||
* Main part of the algorithm
|
||||
* Basic idea:
|
||||
* - Create the initial hull (done in Initialize.cs)
|
||||
*
|
||||
* For each face there are "vertices beyond" which are "visible" from it.
|
||||
* If there are no such vertices, the face is on the hull.
|
||||
*
|
||||
* - While there is at least one face with at least one "vertex beyond":
|
||||
* * Pick the furthest beyond vertex
|
||||
* * For this vertex:
|
||||
* > find all faces that are visible from it (TagAffectedFaces)
|
||||
* > remove them and replace them with a "cone" created by the vertex and the boundary
|
||||
* of the affected faces, and for each new face, compute "beyond vertices"
|
||||
* (CreateCone + CommitCone)
|
||||
*
|
||||
* + Implement it in way that is fast, but hard to understand and maintain.
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Class ConvexHullAlgorithm.
|
||||
/// </summary>
|
||||
internal partial class ConvexHullAlgorithm
|
||||
{
|
||||
/// <summary>
|
||||
/// Tags all faces seen from the current vertex with 1.
|
||||
/// </summary>
|
||||
/// <param name="currentFace"> The current face. </param>
|
||||
private void TagAffectedFaces(ConvexFaceInternal currentFace)
|
||||
{
|
||||
AffectedFaceBuffer.Clear();
|
||||
AffectedFaceBuffer.Add(currentFace.Index);
|
||||
TraverseAffectedFaces(currentFace.Index);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Recursively traverse all the relevant faces.
|
||||
/// </summary>
|
||||
/// <param name="currentFace"> The current face. </param>
|
||||
private void TraverseAffectedFaces(int currentFace)
|
||||
{
|
||||
TraverseStack.Clear();
|
||||
TraverseStack.Push(currentFace);
|
||||
AffectedFaceFlags[currentFace] = true;
|
||||
|
||||
while (TraverseStack.Count > 0)
|
||||
{
|
||||
ConvexFaceInternal top = FacePool[TraverseStack.Pop()];
|
||||
for (int i = 0; i < NumOfDimensions; i++)
|
||||
{
|
||||
int adjFace = top.AdjacentFaces[i];
|
||||
|
||||
if (!AffectedFaceFlags[adjFace] &&
|
||||
MathHelper.GetVertexDistance(CurrentVertex, FacePool[adjFace]) >= PlaneDistanceTolerance)
|
||||
{
|
||||
AffectedFaceBuffer.Add(adjFace);
|
||||
AffectedFaceFlags[adjFace] = true;
|
||||
TraverseStack.Push(adjFace);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new deferred face.
|
||||
/// </summary>
|
||||
/// <param name="face"> The face. </param>
|
||||
/// <param name="faceIndex"> Index of the face. </param>
|
||||
/// <param name="pivot"> The pivot. </param>
|
||||
/// <param name="pivotIndex"> Index of the pivot. </param>
|
||||
/// <param name="oldFace"> The old face. </param>
|
||||
/// <returns> DeferredFace. </returns>
|
||||
private DeferredFace MakeDeferredFace(ConvexFaceInternal face, int faceIndex, ConvexFaceInternal pivot,
|
||||
int pivotIndex, ConvexFaceInternal oldFace)
|
||||
{
|
||||
DeferredFace ret = ObjectManager.GetDeferredFace();
|
||||
|
||||
ret.Face = face;
|
||||
ret.FaceIndex = faceIndex;
|
||||
ret.Pivot = pivot;
|
||||
ret.PivotIndex = pivotIndex;
|
||||
ret.OldFace = oldFace;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Connect faces using a connector.
|
||||
/// </summary>
|
||||
/// <param name="connector"> The connector. </param>
|
||||
private void ConnectFace(FaceConnector connector)
|
||||
{
|
||||
uint index = connector.HashCode % Constants.ConnectorTableSize;
|
||||
ConnectorList list = ConnectorTable[index];
|
||||
|
||||
for (FaceConnector current = list.First; current != null; current = current.Next)
|
||||
{
|
||||
if (FaceConnector.AreConnectable(connector, current, NumOfDimensions))
|
||||
{
|
||||
list.Remove(current);
|
||||
FaceConnector.Connect(current, connector);
|
||||
current.Face = null;
|
||||
connector.Face = null;
|
||||
ObjectManager.DepositConnector(current);
|
||||
ObjectManager.DepositConnector(connector);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
list.Add(connector);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Removes the faces "covered" by the current vertex and adds the newly created ones.
|
||||
/// </summary>
|
||||
/// <returns> <c> true </c> if XXXX, <c> false </c> otherwise. </returns>
|
||||
private bool CreateCone()
|
||||
{
|
||||
int currentVertexIndex = CurrentVertex;
|
||||
ConeFaceBuffer.Clear();
|
||||
|
||||
for (int fIndex = 0; fIndex < AffectedFaceBuffer.Count; fIndex++)
|
||||
{
|
||||
int oldFaceIndex = AffectedFaceBuffer[fIndex];
|
||||
ConvexFaceInternal oldFace = FacePool[oldFaceIndex];
|
||||
|
||||
// Find the faces that need to be updated
|
||||
int updateCount = 0;
|
||||
for (int i = 0; i < NumOfDimensions; i++)
|
||||
{
|
||||
int af = oldFace.AdjacentFaces[i];
|
||||
if (!AffectedFaceFlags[af]) // Tag == false when oldFaces does not contain af
|
||||
{
|
||||
UpdateBuffer[updateCount] = af;
|
||||
UpdateIndices[updateCount] = i;
|
||||
++updateCount;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < updateCount; i++)
|
||||
{
|
||||
ConvexFaceInternal adjacentFace = FacePool[UpdateBuffer[i]];
|
||||
|
||||
int oldFaceAdjacentIndex = 0;
|
||||
int[] adjFaceAdjacency = adjacentFace.AdjacentFaces;
|
||||
for (int j = 0; j < adjFaceAdjacency.Length; j++)
|
||||
{
|
||||
if (oldFaceIndex == adjFaceAdjacency[j])
|
||||
{
|
||||
oldFaceAdjacentIndex = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int forbidden = UpdateIndices[i]; // Index of the face that corresponds to this adjacent face
|
||||
|
||||
int oldVertexIndex;
|
||||
int[] vertices;
|
||||
|
||||
int newFaceIndex = ObjectManager.GetFace();
|
||||
ConvexFaceInternal newFace = FacePool[newFaceIndex];
|
||||
vertices = newFace.Vertices;
|
||||
for (int j = 0; j < NumOfDimensions; j++)
|
||||
{
|
||||
vertices[j] = oldFace.Vertices[j];
|
||||
}
|
||||
|
||||
oldVertexIndex = vertices[forbidden];
|
||||
|
||||
int orderedPivotIndex;
|
||||
|
||||
// correct the ordering
|
||||
if (currentVertexIndex < oldVertexIndex)
|
||||
{
|
||||
orderedPivotIndex = 0;
|
||||
for (int j = forbidden - 1; j >= 0; j--)
|
||||
{
|
||||
if (vertices[j] > currentVertexIndex)
|
||||
{
|
||||
vertices[j + 1] = vertices[j];
|
||||
}
|
||||
else
|
||||
{
|
||||
orderedPivotIndex = j + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
orderedPivotIndex = NumOfDimensions - 1;
|
||||
for (int j = forbidden + 1; j < NumOfDimensions; j++)
|
||||
{
|
||||
if (vertices[j] < currentVertexIndex)
|
||||
{
|
||||
vertices[j - 1] = vertices[j];
|
||||
}
|
||||
else
|
||||
{
|
||||
orderedPivotIndex = j - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vertices[orderedPivotIndex] = CurrentVertex;
|
||||
|
||||
if (!MathHelper.CalculateFacePlane(newFace, Center))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ConeFaceBuffer.Add(MakeDeferredFace(newFace, orderedPivotIndex, adjacentFace, oldFaceAdjacentIndex,
|
||||
oldFace));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Commits a cone and adds a vertex to the convex hull.
|
||||
/// </summary>
|
||||
private void CommitCone()
|
||||
{
|
||||
// Fill the adjacency.
|
||||
for (int i = 0; i < ConeFaceBuffer.Count; i++)
|
||||
{
|
||||
DeferredFace face = ConeFaceBuffer[i];
|
||||
|
||||
ConvexFaceInternal newFace = face.Face;
|
||||
ConvexFaceInternal adjacentFace = face.Pivot;
|
||||
ConvexFaceInternal oldFace = face.OldFace;
|
||||
int orderedPivotIndex = face.FaceIndex;
|
||||
|
||||
newFace.AdjacentFaces[orderedPivotIndex] = adjacentFace.Index;
|
||||
adjacentFace.AdjacentFaces[face.PivotIndex] = newFace.Index;
|
||||
|
||||
// let there be a connection.
|
||||
for (int j = 0; j < NumOfDimensions; j++)
|
||||
{
|
||||
if (j == orderedPivotIndex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
FaceConnector connector = ObjectManager.GetConnector();
|
||||
connector.Update(newFace, j, NumOfDimensions);
|
||||
ConnectFace(connector);
|
||||
}
|
||||
|
||||
// the id adjacent face on the hull? If so, we can use simple method to find beyond vertices.
|
||||
if (adjacentFace.VerticesBeyond.Count == 0)
|
||||
{
|
||||
FindBeyondVertices(newFace, oldFace.VerticesBeyond);
|
||||
}
|
||||
// it is slightly more effective if the face with the lower number of beyond vertices comes first.
|
||||
else if (adjacentFace.VerticesBeyond.Count < oldFace.VerticesBeyond.Count)
|
||||
{
|
||||
FindBeyondVertices(newFace, adjacentFace.VerticesBeyond, oldFace.VerticesBeyond);
|
||||
}
|
||||
else
|
||||
{
|
||||
FindBeyondVertices(newFace, oldFace.VerticesBeyond, adjacentFace.VerticesBeyond);
|
||||
}
|
||||
|
||||
// This face will definitely lie on the hull
|
||||
if (newFace.VerticesBeyond.Count == 0)
|
||||
{
|
||||
ConvexFaces.Add(newFace.Index);
|
||||
UnprocessedFaces.Remove(newFace);
|
||||
ObjectManager.DepositVertexBuffer(newFace.VerticesBeyond);
|
||||
newFace.VerticesBeyond = EmptyBuffer;
|
||||
}
|
||||
else // Add the face to the list
|
||||
{
|
||||
UnprocessedFaces.Add(newFace);
|
||||
}
|
||||
|
||||
// recycle the object.
|
||||
ObjectManager.DepositDeferredFace(face);
|
||||
}
|
||||
|
||||
// Recycle the affected faces.
|
||||
for (int fIndex = 0; fIndex < AffectedFaceBuffer.Count; fIndex++)
|
||||
{
|
||||
int face = AffectedFaceBuffer[fIndex];
|
||||
UnprocessedFaces.Remove(FacePool[face]);
|
||||
ObjectManager.DepositFace(face);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Check whether the vertex v is beyond the given face. If so, add it to beyondVertices.
|
||||
/// </summary>
|
||||
/// <param name="face"> The face. </param>
|
||||
/// <param name="beyondVertices"> The beyond vertices. </param>
|
||||
/// <param name="v"> The v. </param>
|
||||
private void IsBeyond(ConvexFaceInternal face, IndexBuffer beyondVertices, int v)
|
||||
{
|
||||
double distance = MathHelper.GetVertexDistance(v, face);
|
||||
if (distance >= PlaneDistanceTolerance)
|
||||
{
|
||||
if (distance > MaxDistance)
|
||||
{
|
||||
// If it's within the tolerance distance, use the lex. larger point
|
||||
if (distance - MaxDistance < PlaneDistanceTolerance)
|
||||
{
|
||||
// todo: why is this LexCompare necessary. Would seem to favor x over y over z (etc.)?
|
||||
if (LexCompare(v, FurthestVertex) > 0)
|
||||
{
|
||||
MaxDistance = distance;
|
||||
FurthestVertex = v;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MaxDistance = distance;
|
||||
FurthestVertex = v;
|
||||
}
|
||||
}
|
||||
|
||||
beyondVertices.Add(v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Compares the values of two vertices. The return value (-1, 0 or +1) are found
|
||||
/// by first checking the first coordinate and then progressing through the rest.
|
||||
/// In this way {2, 8} will be a "-1" (less than) {3, 1}.
|
||||
/// </summary>
|
||||
/// <param name="u"> The base vertex index, u. </param>
|
||||
/// <param name="v"> The compared vertex index, v. </param>
|
||||
/// <returns> System.Int32. </returns>
|
||||
private int LexCompare(int u, int v)
|
||||
{
|
||||
int uOffset = u * NumOfDimensions, vOffset = v * NumOfDimensions;
|
||||
for (int i = 0; i < NumOfDimensions; i++)
|
||||
{
|
||||
double x = Positions[uOffset + i], y = Positions[vOffset + i];
|
||||
int comp = x.CompareTo(y);
|
||||
if (comp != 0)
|
||||
{
|
||||
return comp;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Used by update faces.
|
||||
/// </summary>
|
||||
/// <param name="face"> The face. </param>
|
||||
/// <param name="beyond"> The beyond. </param>
|
||||
/// <param name="beyond1"> The beyond1. </param>
|
||||
private void FindBeyondVertices(ConvexFaceInternal face, IndexBuffer beyond, IndexBuffer beyond1)
|
||||
{
|
||||
IndexBuffer beyondVertices = BeyondBuffer;
|
||||
|
||||
MaxDistance = double.NegativeInfinity;
|
||||
FurthestVertex = 0;
|
||||
int v;
|
||||
|
||||
for (int i = 0; i < beyond1.Count; i++)
|
||||
{
|
||||
VertexVisited[beyond1[i]] = true;
|
||||
}
|
||||
|
||||
VertexVisited[CurrentVertex] = false;
|
||||
for (int i = 0; i < beyond.Count; i++)
|
||||
{
|
||||
v = beyond[i];
|
||||
if (v == CurrentVertex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
VertexVisited[v] = false;
|
||||
IsBeyond(face, beyondVertices, v);
|
||||
}
|
||||
|
||||
for (int i = 0; i < beyond1.Count; i++)
|
||||
{
|
||||
v = beyond1[i];
|
||||
if (VertexVisited[v])
|
||||
{
|
||||
IsBeyond(face, beyondVertices, v);
|
||||
}
|
||||
}
|
||||
|
||||
face.FurthestVertex = FurthestVertex;
|
||||
|
||||
// Pull the old switch a roo (switch the face beyond buffers)
|
||||
IndexBuffer temp = face.VerticesBeyond;
|
||||
face.VerticesBeyond = beyondVertices;
|
||||
if (temp.Count > 0)
|
||||
{
|
||||
temp.Clear();
|
||||
}
|
||||
|
||||
BeyondBuffer = temp;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Finds the beyond vertices.
|
||||
/// </summary>
|
||||
/// <param name="face"> The face. </param>
|
||||
/// <param name="beyond"> The beyond. </param>
|
||||
private void FindBeyondVertices(ConvexFaceInternal face, IndexBuffer beyond)
|
||||
{
|
||||
IndexBuffer beyondVertices = BeyondBuffer;
|
||||
|
||||
MaxDistance = double.NegativeInfinity;
|
||||
FurthestVertex = 0;
|
||||
int v;
|
||||
|
||||
for (int i = 0; i < beyond.Count; i++)
|
||||
{
|
||||
v = beyond[i];
|
||||
if (v == CurrentVertex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
IsBeyond(face, beyondVertices, v);
|
||||
}
|
||||
|
||||
face.FurthestVertex = FurthestVertex;
|
||||
|
||||
// Pull the old switch a roo (switch the face beyond buffers)
|
||||
IndexBuffer temp = face.VerticesBeyond;
|
||||
face.VerticesBeyond = beyondVertices;
|
||||
if (temp.Count > 0)
|
||||
{
|
||||
temp.Clear();
|
||||
}
|
||||
|
||||
BeyondBuffer = temp;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Recalculates the centroid of the current hull.
|
||||
/// </summary>
|
||||
private void UpdateCenter()
|
||||
{
|
||||
for (int i = 0; i < NumOfDimensions; i++)
|
||||
{
|
||||
Center[i] *= ConvexHullSize;
|
||||
}
|
||||
|
||||
ConvexHullSize += 1;
|
||||
double f = 1.0 / ConvexHullSize;
|
||||
int co = CurrentVertex * NumOfDimensions;
|
||||
for (int i = 0; i < NumOfDimensions; i++)
|
||||
{
|
||||
Center[i] = f * (Center[i] + Positions[co + i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Removes the last vertex from the center.
|
||||
/// </summary>
|
||||
private void RollbackCenter()
|
||||
{
|
||||
for (int i = 0; i < NumOfDimensions; i++)
|
||||
{
|
||||
Center[i] *= ConvexHullSize;
|
||||
}
|
||||
|
||||
ConvexHullSize -= 1;
|
||||
double f = ConvexHullSize > 0 ? 1.0 / ConvexHullSize : 0.0;
|
||||
int co = CurrentVertex * NumOfDimensions;
|
||||
for (int i = 0; i < NumOfDimensions; i++)
|
||||
{
|
||||
Center[i] = f * (Center[i] - Positions[co + i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Handles singular vertex.
|
||||
/// </summary>
|
||||
private void HandleSingular()
|
||||
{
|
||||
RollbackCenter();
|
||||
SingularVertices.Add(CurrentVertex);
|
||||
|
||||
// This means that all the affected faces must be on the hull and that all their "vertices beyond" are
|
||||
// singular.
|
||||
for (int fIndex = 0; fIndex < AffectedFaceBuffer.Count; fIndex++)
|
||||
{
|
||||
ConvexFaceInternal face = FacePool[AffectedFaceBuffer[fIndex]];
|
||||
IndexBuffer vb = face.VerticesBeyond;
|
||||
for (int i = 0; i < vb.Count; i++)
|
||||
{
|
||||
SingularVertices.Add(vb[i]);
|
||||
}
|
||||
|
||||
ConvexFaces.Add(face.Index);
|
||||
UnprocessedFaces.Remove(face);
|
||||
ObjectManager.DepositVertexBuffer(face.VerticesBeyond);
|
||||
face.VerticesBeyond = EmptyBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get a vertex coordinate. In order to reduce speed, all vertex coordinates
|
||||
/// have been placed in a single array.
|
||||
/// </summary>
|
||||
/// <param name="vIndex"> The vertex index. </param>
|
||||
/// <param name="dimension"> The index of the dimension. </param>
|
||||
/// <returns> System.Double. </returns>
|
||||
private double GetCoordinate(int vIndex, int dimension)
|
||||
{
|
||||
return Positions[vIndex * NumOfDimensions + dimension];
|
||||
}
|
||||
|
||||
|
||||
#region Returning the Results in the proper format
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hull vertices.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex"> The type of the t vertex. </typeparam>
|
||||
/// <param name="data"> The data. </param>
|
||||
/// <returns> TVertex[]. </returns>
|
||||
private TVertex[] GetHullVertices<TVertex>(IList<TVertex> data)
|
||||
{
|
||||
int cellCount = ConvexFaces.Count;
|
||||
int hullVertexCount = 0;
|
||||
|
||||
for (int i = 0; i < NumberOfVertices; i++)
|
||||
{
|
||||
VertexVisited[i] = false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < cellCount; i++)
|
||||
{
|
||||
int[] vs = FacePool[ConvexFaces[i]].Vertices;
|
||||
for (int j = 0; j < vs.Length; j++)
|
||||
{
|
||||
int v = vs[j];
|
||||
if (!VertexVisited[v])
|
||||
{
|
||||
VertexVisited[v] = true;
|
||||
hullVertexCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TVertex[] result = new TVertex[hullVertexCount];
|
||||
for (int i = 0; i < NumberOfVertices; i++)
|
||||
{
|
||||
if (VertexVisited[i])
|
||||
{
|
||||
result[--hullVertexCount] = data[i];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Finds the convex hull and creates the TFace objects.
|
||||
/// </summary>
|
||||
/// <typeparam name="TFace"> The type of the t face. </typeparam>
|
||||
/// <typeparam name="TVertex"> The type of the t vertex. </typeparam>
|
||||
/// <returns> TFace[]. </returns>
|
||||
private TFace[] GetConvexFaces<TVertex, TFace>()
|
||||
where TFace : ConvexFace<TVertex, TFace>, new()
|
||||
where TVertex : IVertex
|
||||
{
|
||||
IndexBuffer faces = ConvexFaces;
|
||||
int cellCount = faces.Count;
|
||||
TFace[] cells = new TFace[cellCount];
|
||||
|
||||
for (int i = 0; i < cellCount; i++)
|
||||
{
|
||||
ConvexFaceInternal face = FacePool[faces[i]];
|
||||
TVertex[] vertices = new TVertex[NumOfDimensions];
|
||||
for (int j = 0; j < NumOfDimensions; j++)
|
||||
{
|
||||
vertices[j] = (TVertex)Vertices[face.Vertices[j]];
|
||||
}
|
||||
|
||||
cells[i] = new TFace
|
||||
{
|
||||
Vertices = vertices,
|
||||
Adjacency = new TFace[NumOfDimensions],
|
||||
Normal = IsLifted ? null : face.Normal,
|
||||
};
|
||||
face.Tag = i;
|
||||
}
|
||||
|
||||
for (int i = 0; i < cellCount; i++)
|
||||
{
|
||||
ConvexFaceInternal face = FacePool[faces[i]];
|
||||
TFace cell = cells[i];
|
||||
for (int j = 0; j < NumOfDimensions; j++)
|
||||
{
|
||||
if (face.AdjacentFaces[j] < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
cell.Adjacency[j] = cells[FacePool[face.AdjacentFaces[j]].Tag];
|
||||
}
|
||||
|
||||
// Fix the vertex orientation.
|
||||
if (face.IsNormalFlipped)
|
||||
{
|
||||
TVertex tempVert = cell.Vertices[0];
|
||||
cell.Vertices[0] = cell.Vertices[NumOfDimensions - 1];
|
||||
cell.Vertices[NumOfDimensions - 1] = tempVert;
|
||||
|
||||
TFace tempAdj = cell.Adjacency[0];
|
||||
cell.Adjacency[0] = cell.Adjacency[NumOfDimensions - 1];
|
||||
cell.Adjacency[NumOfDimensions - 1] = tempAdj;
|
||||
}
|
||||
}
|
||||
|
||||
return cells;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5481d13618b5f4b42b5b7b22beac2e69
|
||||
timeCreated: 1490199237
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,257 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* MIConvexHull, Copyright (c) 2015 David Sehnal, Matthew Campbell
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
namespace NWH.DWP2.MiConvexHull
|
||||
{
|
||||
/// <summary>
|
||||
/// For deferred face addition.
|
||||
/// </summary>
|
||||
internal sealed class DeferredFace
|
||||
{
|
||||
/// <summary>
|
||||
/// The faces.
|
||||
/// </summary>
|
||||
public ConvexFaceInternal Face, Pivot, OldFace;
|
||||
|
||||
/// <summary>
|
||||
/// The indices.
|
||||
/// </summary>
|
||||
public int FaceIndex, PivotIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A helper class used to connect faces.
|
||||
/// </summary>
|
||||
internal sealed class FaceConnector
|
||||
{
|
||||
/// <summary>
|
||||
/// The edge to be connected.
|
||||
/// </summary>
|
||||
public int EdgeIndex;
|
||||
|
||||
/// <summary>
|
||||
/// The face.
|
||||
/// </summary>
|
||||
public ConvexFaceInternal Face;
|
||||
|
||||
/// <summary>
|
||||
/// The hash code computed from indices.
|
||||
/// </summary>
|
||||
public uint HashCode;
|
||||
|
||||
/// <summary>
|
||||
/// Next node in the list.
|
||||
/// </summary>
|
||||
public FaceConnector Next;
|
||||
|
||||
/// <summary>
|
||||
/// Prev node in the list.
|
||||
/// </summary>
|
||||
public FaceConnector Previous;
|
||||
|
||||
/// <summary>
|
||||
/// The vertex indices.
|
||||
/// </summary>
|
||||
public int[] Vertices;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Ctor.
|
||||
/// </summary>
|
||||
/// <param name="dimension"> The dimension. </param>
|
||||
public FaceConnector(int dimension)
|
||||
{
|
||||
Vertices = new int[dimension - 1];
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Updates the connector.
|
||||
/// </summary>
|
||||
/// <param name="face"> The face. </param>
|
||||
/// <param name="edgeIndex"> Index of the edge. </param>
|
||||
/// <param name="dim"> The dim. </param>
|
||||
public void Update(ConvexFaceInternal face, int edgeIndex, int dim)
|
||||
{
|
||||
Face = face;
|
||||
EdgeIndex = edgeIndex;
|
||||
|
||||
uint hashCode = 23;
|
||||
|
||||
unchecked
|
||||
{
|
||||
int[] vs = face.Vertices;
|
||||
int i, c = 0;
|
||||
for (i = 0; i < edgeIndex; i++)
|
||||
{
|
||||
Vertices[c++] = vs[i];
|
||||
hashCode += 31 * hashCode + (uint)vs[i];
|
||||
}
|
||||
|
||||
for (i = edgeIndex + 1; i < vs.Length; i++)
|
||||
{
|
||||
Vertices[c++] = vs[i];
|
||||
hashCode += 31 * hashCode + (uint)vs[i];
|
||||
}
|
||||
}
|
||||
|
||||
HashCode = hashCode;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Can two faces be connected.
|
||||
/// </summary>
|
||||
/// <param name="a"> a. </param>
|
||||
/// <param name="b"> The b. </param>
|
||||
/// <param name="dim"> The dim. </param>
|
||||
/// <returns> <c> true </c> if XXXX, <c> false </c> otherwise. </returns>
|
||||
public static bool AreConnectable(FaceConnector a, FaceConnector b, int dim)
|
||||
{
|
||||
if (a.HashCode != b.HashCode)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int[] av = a.Vertices;
|
||||
int[] bv = b.Vertices;
|
||||
for (int i = 0; i < av.Length; i++)
|
||||
{
|
||||
if (av[i] != bv[i])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Connect two faces.
|
||||
/// </summary>
|
||||
/// <param name="a"> a. </param>
|
||||
/// <param name="b"> The b. </param>
|
||||
public static void Connect(FaceConnector a, FaceConnector b)
|
||||
{
|
||||
a.Face.AdjacentFaces[a.EdgeIndex] = b.Face.Index;
|
||||
b.Face.AdjacentFaces[b.EdgeIndex] = a.Face.Index;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This internal class manages the faces of the convex hull. It is a
|
||||
/// separate class from the desired user class.
|
||||
/// </summary>
|
||||
internal sealed class ConvexFaceInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the adjacent face data.
|
||||
/// </summary>
|
||||
public int[] AdjacentFaces;
|
||||
|
||||
/// <summary>
|
||||
/// The furthest vertex.
|
||||
/// </summary>
|
||||
public int FurthestVertex;
|
||||
|
||||
/// <summary>
|
||||
/// Index of the face inside the pool.
|
||||
/// </summary>
|
||||
public int Index;
|
||||
|
||||
/// <summary>
|
||||
/// Is it present in the list.
|
||||
/// </summary>
|
||||
public bool InList;
|
||||
|
||||
/// <summary>
|
||||
/// Is the normal flipped?
|
||||
/// </summary>
|
||||
public bool IsNormalFlipped;
|
||||
|
||||
/// <summary>
|
||||
/// Next node in the list.
|
||||
/// </summary>
|
||||
public ConvexFaceInternal Next;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the normal vector.
|
||||
/// </summary>
|
||||
public double[] Normal;
|
||||
|
||||
/// <summary>
|
||||
/// Face plane constant element.
|
||||
/// </summary>
|
||||
public double Offset;
|
||||
|
||||
//public int UnprocessedIndex;
|
||||
|
||||
/// <summary>
|
||||
/// Prev node in the list.
|
||||
/// </summary>
|
||||
public ConvexFaceInternal Previous;
|
||||
|
||||
/// <summary>
|
||||
/// Used to traverse affected faces and create the Delaunay representation.
|
||||
/// </summary>
|
||||
public int Tag;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the vertices.
|
||||
/// </summary>
|
||||
public int[] Vertices;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the vertices beyond.
|
||||
/// </summary>
|
||||
public IndexBuffer VerticesBeyond;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConvexFaceInternal" /> class.
|
||||
/// </summary>
|
||||
/// <param name="dimension"> The dimension. </param>
|
||||
/// <param name="index"> The index. </param>
|
||||
/// <param name="beyondList"> The beyond list. </param>
|
||||
public ConvexFaceInternal(int dimension, int index, IndexBuffer beyondList)
|
||||
{
|
||||
Index = index;
|
||||
AdjacentFaces = new int[dimension];
|
||||
VerticesBeyond = beyondList;
|
||||
Normal = new double[dimension];
|
||||
Vertices = new int[dimension];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f2a919e301ea5c34da8dd90dc882abdf
|
||||
timeCreated: 1490199238
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,543 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* MIConvexHull, Copyright (c) 2015 David Sehnal, Matthew Campbell
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#region
|
||||
|
||||
using System;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.MiConvexHull
|
||||
{
|
||||
/// <summary>
|
||||
/// A helper class mostly for normal computation. If convex hulls are computed
|
||||
/// in higher dimensions, it might be a good idea to add a specific
|
||||
/// FindNormalVectorND function.
|
||||
/// </summary>
|
||||
internal class MathHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// The dimension
|
||||
/// </summary>
|
||||
private readonly int Dimension;
|
||||
|
||||
/// <summary>
|
||||
/// The matrix pivots
|
||||
/// </summary>
|
||||
private readonly int[] matrixPivots;
|
||||
|
||||
/// <summary>
|
||||
/// The n d matrix
|
||||
/// </summary>
|
||||
private readonly double[] nDMatrix;
|
||||
|
||||
/// <summary>
|
||||
/// The n d normal helper vector
|
||||
/// </summary>
|
||||
private readonly double[] nDNormalHelperVector;
|
||||
|
||||
/// <summary>
|
||||
/// The nt x
|
||||
/// </summary>
|
||||
private readonly double[] ntX;
|
||||
|
||||
/// <summary>
|
||||
/// The nt y
|
||||
/// </summary>
|
||||
private readonly double[] ntY;
|
||||
|
||||
/// <summary>
|
||||
/// The nt z
|
||||
/// </summary>
|
||||
private readonly double[] ntZ;
|
||||
|
||||
/// <summary>
|
||||
/// The position data
|
||||
/// </summary>
|
||||
private readonly double[] PositionData;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MathHelper" /> class.
|
||||
/// </summary>
|
||||
/// <param name="dimension"> The dimension. </param>
|
||||
/// <param name="positions"> The positions. </param>
|
||||
internal MathHelper(int dimension, double[] positions)
|
||||
{
|
||||
PositionData = positions;
|
||||
Dimension = dimension;
|
||||
|
||||
ntX = new double[Dimension];
|
||||
ntY = new double[Dimension];
|
||||
ntZ = new double[Dimension];
|
||||
|
||||
nDNormalHelperVector = new double[Dimension];
|
||||
nDMatrix = new double[Dimension * Dimension];
|
||||
matrixPivots = new int[Dimension];
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the normal and offset of the hyper-plane given by the face's vertices.
|
||||
/// </summary>
|
||||
/// <param name="face"> The face. </param>
|
||||
/// <param name="center"> The center. </param>
|
||||
/// <returns> <c> true </c> if XXXX, <c> false </c> otherwise. </returns>
|
||||
internal bool CalculateFacePlane(ConvexFaceInternal face, double[] center)
|
||||
{
|
||||
int[] vertices = face.Vertices;
|
||||
double[] normal = face.Normal;
|
||||
FindNormalVector(vertices, normal);
|
||||
|
||||
if (double.IsNaN(normal[0]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
double offset = 0.0;
|
||||
double centerDistance = 0.0;
|
||||
int fi = vertices[0] * Dimension;
|
||||
for (int i = 0; i < Dimension; i++)
|
||||
{
|
||||
double n = normal[i];
|
||||
offset += n * PositionData[fi + i];
|
||||
centerDistance += n * center[i];
|
||||
}
|
||||
|
||||
face.Offset = -offset;
|
||||
centerDistance -= offset;
|
||||
|
||||
if (centerDistance > 0)
|
||||
{
|
||||
for (int i = 0; i < Dimension; i++)
|
||||
{
|
||||
normal[i] = -normal[i];
|
||||
}
|
||||
|
||||
face.Offset = offset;
|
||||
face.IsNormalFlipped = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
face.IsNormalFlipped = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Check if the vertex is "visible" from the face.
|
||||
/// The vertex is "over face" if the return value is > Constants.PlaneDistanceTolerance.
|
||||
/// </summary>
|
||||
/// <param name="v"> The v. </param>
|
||||
/// <param name="f"> The f. </param>
|
||||
/// <returns> The vertex is "over face" if the result is positive. </returns>
|
||||
internal double GetVertexDistance(int v, ConvexFaceInternal f)
|
||||
{
|
||||
double[] normal = f.Normal;
|
||||
int x = v * Dimension;
|
||||
double distance = f.Offset;
|
||||
for (int i = 0; i < normal.Length; i++)
|
||||
{
|
||||
distance += normal[i] * PositionData[x + i];
|
||||
}
|
||||
|
||||
return distance;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the vector the between vertices.
|
||||
/// </summary>
|
||||
/// <param name="fromIndex"> From index. </param>
|
||||
/// <param name="toIndex"> To index. </param>
|
||||
/// <param name="target"> The target. </param>
|
||||
/// <returns> </returns>
|
||||
internal double[] VectorBetweenVertices(int toIndex, int fromIndex)
|
||||
{
|
||||
double[] target = new double[Dimension];
|
||||
VectorBetweenVertices(toIndex, fromIndex, target);
|
||||
return target;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the vector the between vertices.
|
||||
/// </summary>
|
||||
/// <param name="fromIndex"> From index. </param>
|
||||
/// <param name="toIndex"> To index. </param>
|
||||
/// <param name="target"> The target. </param>
|
||||
/// <returns> </returns>
|
||||
private void VectorBetweenVertices(int toIndex, int fromIndex, double[] target)
|
||||
{
|
||||
int u = toIndex * Dimension, v = fromIndex * Dimension;
|
||||
for (int i = 0; i < Dimension; i++)
|
||||
{
|
||||
target[i] = PositionData[u + i] - PositionData[v + i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Modified from Math.NET
|
||||
// Copyright (c) 2009-2013 Math.NET
|
||||
/// <summary>
|
||||
/// Lus the factor.
|
||||
/// </summary>
|
||||
/// <param name="data"> The data. </param>
|
||||
/// <param name="order"> The order. </param>
|
||||
/// <param name="ipiv"> The ipiv. </param>
|
||||
/// <param name="vecLUcolj"> The vec l ucolj. </param>
|
||||
private static void LUFactor(double[] data, int order, int[] ipiv, double[] vecLUcolj)
|
||||
{
|
||||
// Initialize the pivot matrix to the identity permutation.
|
||||
for (int i = 0; i < order; i++)
|
||||
{
|
||||
ipiv[i] = i;
|
||||
}
|
||||
|
||||
// Outer loop.
|
||||
for (int j = 0; j < order; j++)
|
||||
{
|
||||
int indexj = j * order;
|
||||
int indexjj = indexj + j;
|
||||
|
||||
// Make a copy of the j-th column to localize references.
|
||||
for (int i = 0; i < order; i++)
|
||||
{
|
||||
vecLUcolj[i] = data[indexj + i];
|
||||
}
|
||||
|
||||
// Apply previous transformations.
|
||||
for (int i = 0; i < order; i++)
|
||||
{
|
||||
// Most of the time is spent in the following dot product.
|
||||
int kmax = Math.Min(i, j);
|
||||
double s = 0.0;
|
||||
for (int k = 0; k < kmax; k++)
|
||||
{
|
||||
s += data[k * order + i] * vecLUcolj[k];
|
||||
}
|
||||
|
||||
data[indexj + i] = vecLUcolj[i] -= s;
|
||||
}
|
||||
|
||||
// Find pivot and exchange if necessary.
|
||||
int p = j;
|
||||
for (int i = j + 1; i < order; i++)
|
||||
{
|
||||
if (Math.Abs(vecLUcolj[i]) > Math.Abs(vecLUcolj[p]))
|
||||
{
|
||||
p = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (p != j)
|
||||
{
|
||||
for (int k = 0; k < order; k++)
|
||||
{
|
||||
int indexk = k * order;
|
||||
int indexkp = indexk + p;
|
||||
int indexkj = indexk + j;
|
||||
double temp = data[indexkp];
|
||||
data[indexkp] = data[indexkj];
|
||||
data[indexkj] = temp;
|
||||
}
|
||||
|
||||
ipiv[j] = p;
|
||||
}
|
||||
|
||||
// Compute multipliers.
|
||||
if ((j < order) & (data[indexjj] != 0.0))
|
||||
{
|
||||
for (int i = j + 1; i < order; i++)
|
||||
{
|
||||
data[indexj + i] /= data[indexjj];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#region Find the normal vector of the face
|
||||
|
||||
/// <summary>
|
||||
/// Finds normal vector of a hyper-plane given by vertices.
|
||||
/// Stores the results to normalData.
|
||||
/// </summary>
|
||||
/// <param name="vertices"> The vertices. </param>
|
||||
/// <param name="normalData"> The normal data. </param>
|
||||
private void FindNormalVector(int[] vertices, double[] normalData)
|
||||
{
|
||||
switch (Dimension)
|
||||
{
|
||||
case 2:
|
||||
FindNormalVector2D(vertices, normalData);
|
||||
break;
|
||||
case 3:
|
||||
FindNormalVector3D(vertices, normalData);
|
||||
break;
|
||||
case 4:
|
||||
FindNormalVector4D(vertices, normalData);
|
||||
break;
|
||||
default:
|
||||
FindNormalVectorND(vertices, normalData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Finds 2D normal vector.
|
||||
/// </summary>
|
||||
/// <param name="vertices"> The vertices. </param>
|
||||
/// <param name="normal"> The normal. </param>
|
||||
private void FindNormalVector2D(int[] vertices, double[] normal)
|
||||
{
|
||||
VectorBetweenVertices(vertices[1], vertices[0], ntX);
|
||||
|
||||
double nx = -ntX[1];
|
||||
double ny = ntX[0];
|
||||
|
||||
double norm = Math.Sqrt(nx * nx + ny * ny);
|
||||
|
||||
double f = 1.0 / norm;
|
||||
normal[0] = f * nx;
|
||||
normal[1] = f * ny;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Finds 3D normal vector.
|
||||
/// </summary>
|
||||
/// <param name="vertices"> The vertices. </param>
|
||||
/// <param name="normal"> The normal. </param>
|
||||
private void FindNormalVector3D(int[] vertices, double[] normal)
|
||||
{
|
||||
VectorBetweenVertices(vertices[1], vertices[0], ntX);
|
||||
VectorBetweenVertices(vertices[2], vertices[1], ntY);
|
||||
|
||||
double nx = ntX[1] * ntY[2] - ntX[2] * ntY[1];
|
||||
double ny = ntX[2] * ntY[0] - ntX[0] * ntY[2];
|
||||
double nz = ntX[0] * ntY[1] - ntX[1] * ntY[0];
|
||||
|
||||
double norm = Math.Sqrt(nx * nx + ny * ny + nz * nz);
|
||||
|
||||
double f = 1.0 / norm;
|
||||
normal[0] = f * nx;
|
||||
normal[1] = f * ny;
|
||||
normal[2] = f * nz;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Finds 4D normal vector.
|
||||
/// </summary>
|
||||
/// <param name="vertices"> The vertices. </param>
|
||||
/// <param name="normal"> The normal. </param>
|
||||
private void FindNormalVector4D(int[] vertices, double[] normal)
|
||||
{
|
||||
VectorBetweenVertices(vertices[1], vertices[0], ntX);
|
||||
VectorBetweenVertices(vertices[2], vertices[1], ntY);
|
||||
VectorBetweenVertices(vertices[3], vertices[2], ntZ);
|
||||
|
||||
double[] x = ntX;
|
||||
double[] y = ntY;
|
||||
double[] z = ntZ;
|
||||
|
||||
// This was generated using Mathematica
|
||||
double nx = x[3] * (y[2] * z[1] - y[1] * z[2])
|
||||
+ x[2] * (y[1] * z[3] - y[3] * z[1])
|
||||
+ x[1] * (y[3] * z[2] - y[2] * z[3]);
|
||||
double ny = x[3] * (y[0] * z[2] - y[2] * z[0])
|
||||
+ x[2] * (y[3] * z[0] - y[0] * z[3])
|
||||
+ x[0] * (y[2] * z[3] - y[3] * z[2]);
|
||||
double nz = x[3] * (y[1] * z[0] - y[0] * z[1])
|
||||
+ x[1] * (y[0] * z[3] - y[3] * z[0])
|
||||
+ x[0] * (y[3] * z[1] - y[1] * z[3]);
|
||||
double nw = x[2] * (y[0] * z[1] - y[1] * z[0])
|
||||
+ x[1] * (y[2] * z[0] - y[0] * z[2])
|
||||
+ x[0] * (y[1] * z[2] - y[2] * z[1]);
|
||||
|
||||
double norm = Math.Sqrt(nx * nx + ny * ny + nz * nz + nw * nw);
|
||||
|
||||
double f = 1.0 / norm;
|
||||
normal[0] = f * nx;
|
||||
normal[1] = f * ny;
|
||||
normal[2] = f * nz;
|
||||
normal[3] = f * nw;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Finds the normal vector nd.
|
||||
/// </summary>
|
||||
/// <param name="vertices"> The vertices. </param>
|
||||
/// <param name="normal"> The normal. </param>
|
||||
private void FindNormalVectorND(int[] vertices, double[] normal)
|
||||
{
|
||||
/* We need to solve the matrix A n = B where
|
||||
* - A contains coordinates of vertices as columns
|
||||
* - B is vector with all 1's. Really, it should be the distance of
|
||||
* the plane from the origin, but - since we're not worried about that
|
||||
* here and we will normalize the normal anyway - all 1's suffices.
|
||||
*/
|
||||
int[] iPiv = matrixPivots;
|
||||
double[] data = nDMatrix;
|
||||
double norm = 0.0;
|
||||
|
||||
// Solve determinants by replacing x-th column by all 1.
|
||||
for (int x = 0; x < Dimension; x++)
|
||||
{
|
||||
for (int i = 0; i < Dimension; i++)
|
||||
{
|
||||
int offset = vertices[i] * Dimension;
|
||||
for (int j = 0; j < Dimension; j++)
|
||||
{
|
||||
// maybe I got the i/j mixed up here regarding the representation Math.net uses...
|
||||
// ...but it does not matter since Det(A) = Det(Transpose(A)).
|
||||
data[Dimension * i + j] = j == x ? 1.0 : PositionData[offset + j];
|
||||
}
|
||||
}
|
||||
|
||||
LUFactor(data, Dimension, iPiv, nDNormalHelperVector);
|
||||
double coord = 1.0;
|
||||
for (int i = 0; i < Dimension; i++)
|
||||
{
|
||||
if (iPiv[i] != i)
|
||||
{
|
||||
coord *= -data[Dimension * i + i]; // the determinant sign changes on row swap.
|
||||
}
|
||||
else
|
||||
{
|
||||
coord *= data[Dimension * i + i];
|
||||
}
|
||||
}
|
||||
|
||||
normal[x] = coord;
|
||||
norm += coord * coord;
|
||||
}
|
||||
|
||||
// Normalize the result
|
||||
double f = 1.0 / Math.Sqrt(norm);
|
||||
for (int i = 0; i < normal.Length; i++)
|
||||
{
|
||||
normal[i] *= f;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Simplex Volume
|
||||
|
||||
/// <summary>
|
||||
/// Gets the simplex volume. Prior to having enough edge vectors, the method pads the remaining with all
|
||||
/// "other numbers". So, yes, this method is not really finding the volume. But a relative volume-like measure. It
|
||||
/// uses the magnitude of the determinant as the volume stand-in following the Cayley-Menger theorem.
|
||||
/// </summary>
|
||||
/// <param name="edgeVectors"> The edge vectors. </param>
|
||||
/// <param name="lastIndex"> The last index. </param>
|
||||
/// <returns> </returns>
|
||||
internal double GetSimplexVolume(double[][] edgeVectors, int lastIndex, double bigNumber)
|
||||
{
|
||||
double[] A = new double[Dimension * Dimension];
|
||||
int index = 0;
|
||||
for (int i = 0; i < Dimension; i++)
|
||||
for (int j = 0; j < Dimension; j++)
|
||||
{
|
||||
if (i <= lastIndex)
|
||||
{
|
||||
A[index++] = edgeVectors[i][j];
|
||||
}
|
||||
else
|
||||
{
|
||||
A[index] = Math.Pow(-1, index) * index++ / bigNumber;
|
||||
}
|
||||
}
|
||||
|
||||
// this last term is used for all the vertices in the comparison for the yet determined vertices
|
||||
// the idea is to come up with sets of numbers that are orthogonal so that an non-zero value will result
|
||||
// and to choose smallish numbers since the choice of vectors will affect what the end volume is.
|
||||
// A better way (todo?) is to solve a smaller matrix. However, cases were found in which the obvious smaller
|
||||
// vector
|
||||
// (the upper left) had too many zeros. So, one would need to find the right subset. Indeed choosing a
|
||||
// subset
|
||||
// biases the first dimensions of the others. Perhaps a larger volume would be created from a different
|
||||
// vertex
|
||||
// if another subset of dimensions were used.
|
||||
return Math.Abs(DeterminantDestructive(A));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Determinants the destructive.
|
||||
/// </summary>
|
||||
/// <param name="buff"> The buff. </param>
|
||||
/// <returns> System.Double. </returns>
|
||||
private double DeterminantDestructive(double[] A)
|
||||
{
|
||||
switch (Dimension)
|
||||
{
|
||||
case 0:
|
||||
return 0.0;
|
||||
case 1:
|
||||
return A[0];
|
||||
case 2:
|
||||
return A[0] * A[3] - A[1] * A[2];
|
||||
case 3:
|
||||
return A[0] * A[4] * A[8] + A[1] * A[5] * A[6] + A[2] * A[3] * A[7]
|
||||
- A[0] * A[5] * A[7] - A[1] * A[3] * A[8] - A[2] * A[4] * A[6];
|
||||
default:
|
||||
{
|
||||
int[] iPiv = new int[Dimension];
|
||||
double[] helper = new double[Dimension];
|
||||
LUFactor(A, Dimension, iPiv, helper);
|
||||
double det = 1.0;
|
||||
for (int i = 0; i < iPiv.Length; i++)
|
||||
{
|
||||
det *= A[Dimension * i + i];
|
||||
if (iPiv[i] != i)
|
||||
{
|
||||
det *= -1; // the determinant sign changes on row swap.
|
||||
}
|
||||
}
|
||||
|
||||
return det;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2ce11a64f3a349f448712e37f44bbbf3
|
||||
timeCreated: 1490199237
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,260 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* MIConvexHull, Copyright (c) 2015 David Sehnal, Matthew Campbell
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#region
|
||||
|
||||
using System;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.MiConvexHull
|
||||
{
|
||||
/// <summary>
|
||||
/// A helper class for object allocation/storage.
|
||||
/// This helps the GC a lot as it prevents the creation of about 75% of
|
||||
/// new face objects (in the case of ConvexFaceInternal). In the case of
|
||||
/// FaceConnectors and DefferedFaces, the difference is even higher (in most
|
||||
/// cases O(1) vs O(number of created faces)).
|
||||
/// </summary>
|
||||
internal class ObjectManager
|
||||
{
|
||||
/// <summary>
|
||||
/// The deferred face stack
|
||||
/// </summary>
|
||||
private readonly SimpleList<DeferredFace> DeferredFaceStack;
|
||||
|
||||
/// <summary>
|
||||
/// The dimension
|
||||
/// </summary>
|
||||
private readonly int Dimension;
|
||||
|
||||
/// <summary>
|
||||
/// The empty buffer stack
|
||||
/// </summary>
|
||||
private readonly SimpleList<IndexBuffer> EmptyBufferStack;
|
||||
|
||||
/// <summary>
|
||||
/// The free face indices
|
||||
/// </summary>
|
||||
private readonly IndexBuffer FreeFaceIndices;
|
||||
|
||||
/// <summary>
|
||||
/// The hull
|
||||
/// </summary>
|
||||
private readonly ConvexHullAlgorithm Hull;
|
||||
|
||||
/// <summary>
|
||||
/// The connector stack
|
||||
/// </summary>
|
||||
private FaceConnector ConnectorStack;
|
||||
|
||||
/// <summary>
|
||||
/// The face pool
|
||||
/// </summary>
|
||||
private ConvexFaceInternal[] FacePool;
|
||||
|
||||
/// <summary>
|
||||
/// The face pool capacity
|
||||
/// </summary>
|
||||
private int FacePoolCapacity;
|
||||
|
||||
/// <summary>
|
||||
/// The face pool size
|
||||
/// </summary>
|
||||
private int FacePoolSize;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create the manager.
|
||||
/// </summary>
|
||||
/// <param name="hull"> The hull. </param>
|
||||
public ObjectManager(ConvexHullAlgorithm hull)
|
||||
{
|
||||
Dimension = hull.NumOfDimensions;
|
||||
Hull = hull;
|
||||
FacePool = hull.FacePool;
|
||||
FacePoolSize = 0;
|
||||
FacePoolCapacity = hull.FacePool.Length;
|
||||
FreeFaceIndices = new IndexBuffer();
|
||||
|
||||
EmptyBufferStack = new SimpleList<IndexBuffer>();
|
||||
DeferredFaceStack = new SimpleList<DeferredFace>();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Return the face to the pool for later use.
|
||||
/// </summary>
|
||||
/// <param name="faceIndex"> Index of the face. </param>
|
||||
public void DepositFace(int faceIndex)
|
||||
{
|
||||
ConvexFaceInternal face = FacePool[faceIndex];
|
||||
int[] af = face.AdjacentFaces;
|
||||
for (int i = 0; i < af.Length; i++)
|
||||
{
|
||||
af[i] = -1;
|
||||
}
|
||||
|
||||
FreeFaceIndices.Push(faceIndex);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reallocate the face pool, including the AffectedFaceFlags
|
||||
/// </summary>
|
||||
private void ReallocateFacePool()
|
||||
{
|
||||
ConvexFaceInternal[] newPool = new ConvexFaceInternal[2 * FacePoolCapacity];
|
||||
bool[] newTags = new bool[2 * FacePoolCapacity];
|
||||
Array.Copy(FacePool, newPool, FacePoolCapacity);
|
||||
Buffer.BlockCopy(Hull.AffectedFaceFlags, 0, newTags, 0, FacePoolCapacity * sizeof(bool));
|
||||
FacePoolCapacity = 2 * FacePoolCapacity;
|
||||
Hull.FacePool = newPool;
|
||||
FacePool = newPool;
|
||||
Hull.AffectedFaceFlags = newTags;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create a new face and put it in the pool.
|
||||
/// </summary>
|
||||
/// <returns> System.Int32. </returns>
|
||||
private int CreateFace()
|
||||
{
|
||||
int index = FacePoolSize;
|
||||
ConvexFaceInternal face = new(Dimension, index, GetVertexBuffer());
|
||||
FacePoolSize++;
|
||||
if (FacePoolSize > FacePoolCapacity)
|
||||
{
|
||||
ReallocateFacePool();
|
||||
}
|
||||
|
||||
FacePool[index] = face;
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Return index of an unused face or creates a new one.
|
||||
/// </summary>
|
||||
/// <returns> System.Int32. </returns>
|
||||
public int GetFace()
|
||||
{
|
||||
if (FreeFaceIndices.Count > 0)
|
||||
{
|
||||
return FreeFaceIndices.Pop();
|
||||
}
|
||||
|
||||
return CreateFace();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Store a face connector in the "embedded" linked list.
|
||||
/// </summary>
|
||||
/// <param name="connector"> The connector. </param>
|
||||
public void DepositConnector(FaceConnector connector)
|
||||
{
|
||||
if (ConnectorStack == null)
|
||||
{
|
||||
connector.Next = null;
|
||||
ConnectorStack = connector;
|
||||
}
|
||||
else
|
||||
{
|
||||
connector.Next = ConnectorStack;
|
||||
ConnectorStack = connector;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get an unused face connector. If none is available, create it.
|
||||
/// </summary>
|
||||
/// <returns> FaceConnector. </returns>
|
||||
public FaceConnector GetConnector()
|
||||
{
|
||||
if (ConnectorStack == null)
|
||||
{
|
||||
return new FaceConnector(Dimension);
|
||||
}
|
||||
|
||||
FaceConnector ret = ConnectorStack;
|
||||
ConnectorStack = ConnectorStack.Next;
|
||||
ret.Next = null;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Deposit the index buffer.
|
||||
/// </summary>
|
||||
/// <param name="buffer"> The buffer. </param>
|
||||
public void DepositVertexBuffer(IndexBuffer buffer)
|
||||
{
|
||||
buffer.Clear();
|
||||
EmptyBufferStack.Push(buffer);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get a store index buffer or create a new instance.
|
||||
/// </summary>
|
||||
/// <returns> IndexBuffer. </returns>
|
||||
public IndexBuffer GetVertexBuffer()
|
||||
{
|
||||
return EmptyBufferStack.Count != 0 ? EmptyBufferStack.Pop() : new IndexBuffer();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Deposit the deferred face.
|
||||
/// </summary>
|
||||
/// <param name="face"> The face. </param>
|
||||
public void DepositDeferredFace(DeferredFace face)
|
||||
{
|
||||
DeferredFaceStack.Push(face);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get the deferred face.
|
||||
/// </summary>
|
||||
/// <returns> DeferredFace. </returns>
|
||||
public DeferredFace GetDeferredFace()
|
||||
{
|
||||
return DeferredFaceStack.Count != 0 ? DeferredFaceStack.Pop() : new DeferredFace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d85ac2afa1f0644483ae367df38e2c3
|
||||
timeCreated: 1490199237
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,61 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* MIConvexHull, Copyright (c) 2015 David Sehnal, Matthew Campbell
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
namespace NWH.DWP2.MiConvexHull
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface for a structure with nD position.
|
||||
/// </summary>
|
||||
public interface IVertex
|
||||
{
|
||||
/// <summary>
|
||||
/// Position of the vertex.
|
||||
/// </summary>
|
||||
/// <value> The position. </value>
|
||||
double[] Position { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// "Default" vertex.
|
||||
/// </summary>
|
||||
/// <seealso cref="NWH.DWP2.MiConvexHull.IVertex" />
|
||||
public class DefaultVertex : IVertex
|
||||
{
|
||||
/// <summary>
|
||||
/// Position of the vertex.
|
||||
/// </summary>
|
||||
/// <value> The position. </value>
|
||||
public double[] Position { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 55cca5d242b599546bef109b0dcb77b8
|
||||
timeCreated: 1490199237
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2010 David Sehnal, Matthew Campbell
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a52199677cb510241956083f04f1f2f4
|
||||
timeCreated: 1490199238
|
||||
licenseType: Store
|
||||
TextScriptImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,184 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* MIConvexHull, Copyright (c) 2015 David Sehnal, Matthew Campbell
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#region
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.MiConvexHull
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple interface to unify different types of triangulations in the future.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex"> The type of the t vertex. </typeparam>
|
||||
/// <typeparam name="TCell"> The type of the t cell. </typeparam>
|
||||
public interface ITriangulation<TVertex, TCell>
|
||||
where TCell : TriangulationCell<TVertex, TCell>, new()
|
||||
where TVertex : IVertex
|
||||
{
|
||||
/// <summary>
|
||||
/// Triangulation simplexes. For 2D - triangles, 3D - tetrahedrons, etc ...
|
||||
/// </summary>
|
||||
/// <value> The cells. </value>
|
||||
IEnumerable<TCell> Cells { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Factory class for creating triangulations.
|
||||
/// </summary>
|
||||
public static class Triangulation
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates the Delaunay triangulation of the input data.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex"> The type of the t vertex. </typeparam>
|
||||
/// <param name="data"> The data. </param>
|
||||
/// <param name="config"> If null, default TriangulationComputationConfig is used. </param>
|
||||
/// <returns> ITriangulation<TVertex, DefaultTriangulationCell<TVertex>>. </returns>
|
||||
public static ITriangulation<TVertex, DefaultTriangulationCell<TVertex>> CreateDelaunay<TVertex>(
|
||||
IList<TVertex> data)
|
||||
where TVertex : IVertex
|
||||
{
|
||||
return DelaunayTriangulation<TVertex, DefaultTriangulationCell<TVertex>>.Create(data);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates the Delaunay triangulation of the input data.
|
||||
/// </summary>
|
||||
/// <param name="data"> The data. </param>
|
||||
/// <param name="config"> If null, default TriangulationComputationConfig is used. </param>
|
||||
/// <returns> ITriangulation<DefaultVertex, DefaultTriangulationCell<DefaultVertex>>. </returns>
|
||||
public static ITriangulation<DefaultVertex, DefaultTriangulationCell<DefaultVertex>> CreateDelaunay(
|
||||
IList<double[]> data)
|
||||
{
|
||||
List<DefaultVertex> points = data.Select(p => new DefaultVertex { Position = p, }).ToList();
|
||||
return DelaunayTriangulation<DefaultVertex, DefaultTriangulationCell<DefaultVertex>>.Create(points);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates the Delaunay triangulation of the input data.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex"> </typeparam>
|
||||
/// <typeparam name="TFace"> </typeparam>
|
||||
/// <param name="data"> </param>
|
||||
/// <param name="config"> If null, default TriangulationComputationConfig is used. </param>
|
||||
/// <returns> </returns>
|
||||
public static ITriangulation<TVertex, TFace> CreateDelaunay<TVertex, TFace>(IList<TVertex> data)
|
||||
where TVertex : IVertex
|
||||
where TFace : TriangulationCell<TVertex, TFace>, new()
|
||||
{
|
||||
return DelaunayTriangulation<TVertex, TFace>.Create(data);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create the voronoi mesh.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex"> </typeparam>
|
||||
/// <typeparam name="TCell"> </typeparam>
|
||||
/// <typeparam name="TEdge"> </typeparam>
|
||||
/// <param name="data"> </param>
|
||||
/// <param name="config"> If null, default TriangulationComputationConfig is used. </param>
|
||||
/// <returns> </returns>
|
||||
public static VoronoiMesh<TVertex, TCell, TEdge> CreateVoronoi<TVertex, TCell, TEdge>(IList<TVertex> data)
|
||||
where TCell : TriangulationCell<TVertex, TCell>, new()
|
||||
where TVertex : IVertex
|
||||
where TEdge : VoronoiEdge<TVertex, TCell>, new()
|
||||
{
|
||||
return VoronoiMesh<TVertex, TCell, TEdge>.Create(data);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create the voronoi mesh.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex"> </typeparam>
|
||||
/// <param name="data"> </param>
|
||||
/// <param name="config"> If null, default TriangulationComputationConfig is used. </param>
|
||||
/// <returns> </returns>
|
||||
public static
|
||||
VoronoiMesh
|
||||
<TVertex, DefaultTriangulationCell<TVertex>, VoronoiEdge<TVertex, DefaultTriangulationCell<TVertex>>>
|
||||
CreateVoronoi<TVertex>(IList<TVertex> data)
|
||||
where TVertex : IVertex
|
||||
{
|
||||
return
|
||||
VoronoiMesh
|
||||
<TVertex, DefaultTriangulationCell<TVertex>, VoronoiEdge<TVertex, DefaultTriangulationCell<TVertex>>
|
||||
>.Create(data);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create the voronoi mesh.
|
||||
/// </summary>
|
||||
/// <param name="data"> </param>
|
||||
/// <param name="config"> If null, default TriangulationComputationConfig is used. </param>
|
||||
/// <returns> </returns>
|
||||
public static
|
||||
VoronoiMesh
|
||||
<DefaultVertex, DefaultTriangulationCell<DefaultVertex>,
|
||||
VoronoiEdge<DefaultVertex, DefaultTriangulationCell<DefaultVertex>>>
|
||||
CreateVoronoi(IList<double[]> data)
|
||||
{
|
||||
List<DefaultVertex> points = data.Select(p => new DefaultVertex { Position = p.ToArray(), }).ToList();
|
||||
return
|
||||
VoronoiMesh
|
||||
<DefaultVertex, DefaultTriangulationCell<DefaultVertex>,
|
||||
VoronoiEdge<DefaultVertex, DefaultTriangulationCell<DefaultVertex>>>.Create(points);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create the voronoi mesh.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex"> </typeparam>
|
||||
/// <typeparam name="TCell"> </typeparam>
|
||||
/// <param name="data"> </param>
|
||||
/// <param name="config"> If null, default TriangulationComputationConfig is used. </param>
|
||||
/// <returns> </returns>
|
||||
public static VoronoiMesh<TVertex, TCell, VoronoiEdge<TVertex, TCell>> CreateVoronoi<TVertex, TCell>(
|
||||
IList<TVertex> data)
|
||||
where TVertex : IVertex
|
||||
where TCell : TriangulationCell<TVertex, TCell>, new()
|
||||
{
|
||||
return VoronoiMesh<TVertex, TCell, VoronoiEdge<TVertex, TCell>>.Create(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 895b4d962c8ab954badafb72a29ec508
|
||||
timeCreated: 1490199238
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 600e58bff5c3123429999ef4addc1d01
|
||||
folderAsset: yes
|
||||
timeCreated: 1490199232
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,108 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* MIConvexHull, Copyright (c) 2015 David Sehnal, Matthew Campbell
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#region
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.MiConvexHull
|
||||
{
|
||||
/*
|
||||
* Code here handles triangulation related stuff.
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Class ConvexHullAlgorithm.
|
||||
/// </summary>
|
||||
internal partial class ConvexHullAlgorithm
|
||||
{
|
||||
/// <summary>
|
||||
/// Computes the Delaunay triangulation.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex"> The type of the t vertex. </typeparam>
|
||||
/// <typeparam name="TCell"> The type of the t cell. </typeparam>
|
||||
/// <param name="data"> The data. </param>
|
||||
/// <returns> TCell[]. </returns>
|
||||
internal static TCell[] GetDelaunayTriangulation<TVertex, TCell>(IList<TVertex> data)
|
||||
where TCell : TriangulationCell<TVertex, TCell>, new()
|
||||
where TVertex : IVertex
|
||||
{
|
||||
ConvexHullAlgorithm ch = new(data.Cast<IVertex>().ToArray(), true, Constants.DefaultPlaneDistanceTolerance);
|
||||
ch.GetConvexHull();
|
||||
ch.RemoveUpperFaces();
|
||||
return ch.GetConvexFaces<TVertex, TCell>();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Removes up facing Tetrahedrons from the triangulation.
|
||||
/// </summary>
|
||||
private void RemoveUpperFaces()
|
||||
{
|
||||
IndexBuffer delaunayFaces = ConvexFaces;
|
||||
int dimension = NumOfDimensions - 1;
|
||||
|
||||
// Remove the "upper" faces
|
||||
for (int i = delaunayFaces.Count - 1; i >= 0; i--)
|
||||
{
|
||||
int candidateIndex = delaunayFaces[i];
|
||||
ConvexFaceInternal candidate = FacePool[candidateIndex];
|
||||
if (candidate.Normal[dimension] >= 0.0)
|
||||
{
|
||||
for (int fi = 0; fi < candidate.AdjacentFaces.Length; fi++)
|
||||
{
|
||||
int af = candidate.AdjacentFaces[fi];
|
||||
if (af >= 0)
|
||||
{
|
||||
ConvexFaceInternal face = FacePool[af];
|
||||
for (int j = 0; j < face.AdjacentFaces.Length; j++)
|
||||
{
|
||||
if (face.AdjacentFaces[j] == candidateIndex)
|
||||
{
|
||||
face.AdjacentFaces[j] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delaunayFaces[i] = delaunayFaces[delaunayFaces.Count - 1];
|
||||
delaunayFaces.Pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f82a31334d2c0d04cb8f8cf0a1040eec
|
||||
timeCreated: 1490199238
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,93 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* MIConvexHull, Copyright (c) 2015 David Sehnal, Matthew Campbell
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#region
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.MiConvexHull
|
||||
{
|
||||
/// <summary>
|
||||
/// Calculation and representation of Delaunay triangulation.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex"> The type of the t vertex. </typeparam>
|
||||
/// <typeparam name="TCell"> The type of the t cell. </typeparam>
|
||||
/// <seealso cref="NWH.DWP2.MiConvexHull.ITriangulation{TVertex, TCell}" />
|
||||
public class DelaunayTriangulation<TVertex, TCell> : ITriangulation<TVertex, TCell>
|
||||
where TCell : TriangulationCell<TVertex, TCell>, new()
|
||||
where TVertex : IVertex
|
||||
{
|
||||
/// <summary>
|
||||
/// Can only be created using a factory method.
|
||||
/// </summary>
|
||||
private DelaunayTriangulation()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Cells of the triangulation.
|
||||
/// </summary>
|
||||
/// <value> The cells. </value>
|
||||
public IEnumerable<TCell> Cells { get; private set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates the Delaunay triangulation of the input data.
|
||||
/// </summary>
|
||||
/// <param name="data"> The data. </param>
|
||||
/// <param name="config"> If null, default ConvexHullComputationConfig is used. </param>
|
||||
/// <returns> DelaunayTriangulation<TVertex, TCell>. </returns>
|
||||
/// <exception cref="ArgumentNullException"> data </exception>
|
||||
public static DelaunayTriangulation<TVertex, TCell> Create(IList<TVertex> data)
|
||||
{
|
||||
if (data == null)
|
||||
{
|
||||
throw new ArgumentNullException("data");
|
||||
}
|
||||
|
||||
if (data.Count == 0)
|
||||
{
|
||||
return new DelaunayTriangulation<TVertex, TCell> { Cells = new TCell[0], };
|
||||
}
|
||||
|
||||
TCell[] cells = ConvexHullAlgorithm.GetDelaunayTriangulation<TVertex, TCell>(data);
|
||||
|
||||
return new DelaunayTriangulation<TVertex, TCell> { Cells = cells, };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 88239ca96afb6c34a952168af355e2f1
|
||||
timeCreated: 1490199238
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,59 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* MIConvexHull, Copyright (c) 2015 David Sehnal, Matthew Campbell
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
namespace NWH.DWP2.MiConvexHull
|
||||
{
|
||||
/// <summary>
|
||||
/// Representation of the triangulation cell. Pretty much the same as ConvexFace,
|
||||
/// just wanted to distinguish the two.
|
||||
/// To declare your own face type, use class Face : DelaunayFace(of Vertex, of Face)
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex"> The type of the t vertex. </typeparam>
|
||||
/// <typeparam name="TCell"> The type of the t cell. </typeparam>
|
||||
/// <seealso cref="NWH.DWP2.MiConvexHull.ConvexFace{TVertex, TCell}" />
|
||||
public abstract class TriangulationCell<TVertex, TCell> : ConvexFace<TVertex, TCell>
|
||||
where TVertex : IVertex
|
||||
where TCell : ConvexFace<TVertex, TCell>
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default triangulation cell that inherits from TriangulationCell with self-referencing type parameter.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex"> The type of the vertex. </typeparam>
|
||||
public class DefaultTriangulationCell<TVertex> : TriangulationCell<TVertex, DefaultTriangulationCell<TVertex>>
|
||||
where TVertex : IVertex
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aea7d87dcfb14b14ab63eb32dd4dac36
|
||||
timeCreated: 1490199238
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,116 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* MIConvexHull, Copyright (c) 2015 David Sehnal, Matthew Campbell
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
namespace NWH.DWP2.MiConvexHull
|
||||
{
|
||||
/// <summary>
|
||||
/// A class representing an (undirected) edge of the Voronoi graph.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex"> The type of the t vertex. </typeparam>
|
||||
/// <typeparam name="TCell"> The type of the t cell. </typeparam>
|
||||
public class VoronoiEdge<TVertex, TCell>
|
||||
where TVertex : IVertex
|
||||
where TCell : TriangulationCell<TVertex, TCell>
|
||||
{
|
||||
/// <summary>
|
||||
/// Create an instance of the edge.
|
||||
/// </summary>
|
||||
public VoronoiEdge()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of the edge.
|
||||
/// </summary>
|
||||
/// <param name="source"> The source. </param>
|
||||
/// <param name="target"> The target. </param>
|
||||
public VoronoiEdge(TCell source, TCell target)
|
||||
{
|
||||
Source = source;
|
||||
Target = target;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Source of the edge.
|
||||
/// </summary>
|
||||
/// <value> The source. </value>
|
||||
public TCell Source { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Target of the edge.
|
||||
/// </summary>
|
||||
/// <value> The target. </value>
|
||||
public TCell Target { get; internal set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// ...
|
||||
/// </summary>
|
||||
/// <param name="obj"> The object to compare with the current object. </param>
|
||||
/// <returns>
|
||||
/// <c> true </c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise,
|
||||
/// <c> false </c>.
|
||||
/// </returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
VoronoiEdge<TVertex, TCell> other = obj as VoronoiEdge<TVertex, TCell>;
|
||||
if (other == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(this, other))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return (Source == other.Source && Target == other.Target)
|
||||
|| (Source == other.Target && Target == other.Source);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// ...
|
||||
/// </summary>
|
||||
/// <returns> A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. </returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
int hash = 23;
|
||||
hash = hash * 31 + Source.GetHashCode();
|
||||
return hash * 31 + Target.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d2b67703e22414246bc396d0d8a8f971
|
||||
timeCreated: 1490199238
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,231 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* MIConvexHull, Copyright (c) 2015 David Sehnal, Matthew Campbell
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#region
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.MiConvexHull
|
||||
{
|
||||
/// <summary>
|
||||
/// A factory class for creating a Voronoi mesh.
|
||||
/// </summary>
|
||||
public static class VoronoiMesh
|
||||
{
|
||||
/// <summary>
|
||||
/// Create the voronoi mesh.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex"> The type of the t vertex. </typeparam>
|
||||
/// <typeparam name="TCell"> The type of the t cell. </typeparam>
|
||||
/// <typeparam name="TEdge"> The type of the t edge. </typeparam>
|
||||
/// <param name="data"> The data. </param>
|
||||
/// <param name="config"> If null, default TriangulationComputationConfig is used. </param>
|
||||
/// <returns> VoronoiMesh<TVertex, TCell, TEdge>. </returns>
|
||||
public static VoronoiMesh<TVertex, TCell, TEdge> Create<TVertex, TCell, TEdge>(IList<TVertex> data)
|
||||
where TCell : TriangulationCell<TVertex, TCell>, new()
|
||||
where TVertex : IVertex
|
||||
where TEdge : VoronoiEdge<TVertex, TCell>, new()
|
||||
{
|
||||
return VoronoiMesh<TVertex, TCell, TEdge>.Create(data);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create the voronoi mesh.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex"> The type of the t vertex. </typeparam>
|
||||
/// <param name="data"> The data. </param>
|
||||
/// <param name="config"> If null, default TriangulationComputationConfig is used. </param>
|
||||
/// <returns>
|
||||
/// VoronoiMesh<TVertex, DefaultTriangulationCell<TVertex>, VoronoiEdge<TVertex,
|
||||
/// DefaultTriangulationCell<TVertex>>>.
|
||||
/// </returns>
|
||||
public static
|
||||
VoronoiMesh
|
||||
<TVertex, DefaultTriangulationCell<TVertex>, VoronoiEdge<TVertex, DefaultTriangulationCell<TVertex>>>
|
||||
Create<TVertex>(IList<TVertex> data)
|
||||
where TVertex : IVertex
|
||||
{
|
||||
return
|
||||
VoronoiMesh
|
||||
<TVertex, DefaultTriangulationCell<TVertex>, VoronoiEdge<TVertex, DefaultTriangulationCell<TVertex>>
|
||||
>.Create(data);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create the voronoi mesh.
|
||||
/// </summary>
|
||||
/// <param name="data"> The data. </param>
|
||||
/// <param name="config"> If null, default TriangulationComputationConfig is used. </param>
|
||||
/// <returns>
|
||||
/// VoronoiMesh<DefaultVertex, DefaultTriangulationCell<DefaultVertex>, VoronoiEdge<DefaultVertex,
|
||||
/// DefaultTriangulationCell<DefaultVertex>>>.
|
||||
/// </returns>
|
||||
public static
|
||||
VoronoiMesh
|
||||
<DefaultVertex, DefaultTriangulationCell<DefaultVertex>,
|
||||
VoronoiEdge<DefaultVertex, DefaultTriangulationCell<DefaultVertex>>>
|
||||
Create(IList<double[]> data)
|
||||
{
|
||||
List<DefaultVertex> points = data.Select(p => new DefaultVertex { Position = p.ToArray(), }).ToList();
|
||||
return
|
||||
VoronoiMesh
|
||||
<DefaultVertex, DefaultTriangulationCell<DefaultVertex>,
|
||||
VoronoiEdge<DefaultVertex, DefaultTriangulationCell<DefaultVertex>>>.Create(points);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create the voronoi mesh.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex"> The type of the t vertex. </typeparam>
|
||||
/// <typeparam name="TCell"> The type of the t cell. </typeparam>
|
||||
/// <param name="data"> The data. </param>
|
||||
/// <param name="config"> If null, default TriangulationComputationConfig is used. </param>
|
||||
/// <returns> VoronoiMesh<TVertex, TCell, VoronoiEdge<TVertex, TCell>>. </returns>
|
||||
public static VoronoiMesh<TVertex, TCell, VoronoiEdge<TVertex, TCell>> Create<TVertex, TCell>(
|
||||
IList<TVertex> data)
|
||||
where TVertex : IVertex
|
||||
where TCell : TriangulationCell<TVertex, TCell>, new()
|
||||
{
|
||||
return VoronoiMesh<TVertex, TCell, VoronoiEdge<TVertex, TCell>>.Create(data);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A representation of a voronoi mesh.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEdge"> The type of the t edge. </typeparam>
|
||||
/// <typeparam name="TVertex"> The type of the t vertex. </typeparam>
|
||||
/// <typeparam name="TCell"> The type of the t cell. </typeparam>
|
||||
public class VoronoiMesh<TVertex, TCell, TEdge>
|
||||
where TCell : TriangulationCell<TVertex, TCell>, new()
|
||||
where TVertex : IVertex
|
||||
where TEdge : VoronoiEdge<TVertex, TCell>, new()
|
||||
{
|
||||
/// <summary>
|
||||
/// Can only be created using a factory method.
|
||||
/// </summary>
|
||||
private VoronoiMesh()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Vertices of the diagram.
|
||||
/// </summary>
|
||||
/// <value> The vertices. </value>
|
||||
public IEnumerable<TCell> Vertices { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Edges connecting the cells.
|
||||
/// The same information can be retrieved Cells' Adjacency.
|
||||
/// </summary>
|
||||
/// <value> The edges. </value>
|
||||
public IEnumerable<TEdge> Edges { get; private set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create a Voronoi diagram of the input data.
|
||||
/// </summary>
|
||||
/// <param name="data"> The data. </param>
|
||||
/// <param name="config"> If null, default TriangulationComputationConfig is used. </param>
|
||||
/// <returns> VoronoiMesh<TVertex, TCell, TEdge>. </returns>
|
||||
/// <exception cref="ArgumentNullException"> data </exception>
|
||||
public static VoronoiMesh<TVertex, TCell, TEdge> Create(IList<TVertex> data)
|
||||
{
|
||||
if (data == null)
|
||||
{
|
||||
throw new ArgumentNullException("data");
|
||||
}
|
||||
|
||||
DelaunayTriangulation<TVertex, TCell> t = DelaunayTriangulation<TVertex, TCell>.Create(data);
|
||||
List<TCell> vertices = t.Cells.ToList();
|
||||
HashSet<TEdge> edges = new(new EdgeComparer());
|
||||
|
||||
foreach (TCell f in vertices)
|
||||
{
|
||||
for (int i = 0; i < f.Adjacency.Length; i++)
|
||||
{
|
||||
TCell af = f.Adjacency[i];
|
||||
if (af != null)
|
||||
{
|
||||
edges.Add(new TEdge { Source = f, Target = af, });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new VoronoiMesh<TVertex, TCell, TEdge>
|
||||
{
|
||||
Vertices = vertices,
|
||||
Edges = edges.ToList(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This is probably not needed, but might make things a tiny bit faster.
|
||||
/// </summary>
|
||||
/// <seealso cref="System.Collections.Generic.IEqualityComparer{TEdge}" />
|
||||
private class EdgeComparer : IEqualityComparer<TEdge>
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether the specified objects are equal.
|
||||
/// </summary>
|
||||
/// <param name="x"> The first object of type <paramref name="T" /> to compare. </param>
|
||||
/// <param name="y"> The second object of type <paramref name="T" /> to compare. </param>
|
||||
/// <returns> true if the specified objects are equal; otherwise, false. </returns>
|
||||
public bool Equals(TEdge x, TEdge y)
|
||||
{
|
||||
return (x.Source == y.Source && x.Target == y.Target) || (x.Source == y.Target && x.Target == y.Source);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code for this instance.
|
||||
/// </summary>
|
||||
/// <param name="obj"> The <see cref="T:System.Object" /> for which a hash code is to be returned. </param>
|
||||
/// <returns> A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. </returns>
|
||||
public int GetHashCode(TEdge obj)
|
||||
{
|
||||
return obj.Source.GetHashCode() ^ obj.Target.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7967e35aa5e1f4a44a6d126181a5bc69
|
||||
timeCreated: 1490199238
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,59 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
#region
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.MiConvexHull
|
||||
{
|
||||
/// <summary>
|
||||
/// Unity-specific vertex implementation for convex hull calculations.
|
||||
/// </summary>
|
||||
public class Vertex : IVertex
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new vertex with specified coordinates.
|
||||
/// </summary>
|
||||
/// <param name="x">X coordinate.</param>
|
||||
/// <param name="y">Y coordinate.</param>
|
||||
/// <param name="z">Z coordinate.</param>
|
||||
public Vertex(double x, double y, double z)
|
||||
{
|
||||
Position = new double[3] { x, y, z, };
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new vertex from a Unity Vector3.
|
||||
/// </summary>
|
||||
/// <param name="ver">Unity Vector3 position.</param>
|
||||
public Vertex(Vector3 ver)
|
||||
{
|
||||
Position = new double[3] { ver.x, ver.y, ver.z, };
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Position of the vertex in 3D space.
|
||||
/// </summary>
|
||||
public double[] Position { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Converts the vertex position to a Unity Vector3.
|
||||
/// </summary>
|
||||
/// <returns>Unity Vector3 representation of the vertex position.</returns>
|
||||
public Vector3 ToVec()
|
||||
{
|
||||
return new Vector3((float)Position[0], (float)Position[1], (float)Position[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a7aea071fc0ba4547849896a0f7f2cc2
|
||||
timeCreated: 1490199448
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 19d46e96be20776428af8cb44d2a5620
|
||||
folderAsset: yes
|
||||
timeCreated: 1493253685
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,49 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
#region
|
||||
|
||||
using System.Collections;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.MeshDecimation
|
||||
{
|
||||
/// <summary>
|
||||
/// Comparer for sorting vertices by their edge collapse cost during mesh decimation.
|
||||
/// </summary>
|
||||
public class Comparer : IComparer
|
||||
{
|
||||
private Vert vx;
|
||||
private Vert vy;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Compares two vertices based on their edge collapse cost.
|
||||
/// </summary>
|
||||
/// <param name="x">First vertex to compare.</param>
|
||||
/// <param name="y">Second vertex to compare.</param>
|
||||
/// <returns>-1 if x has lower cost, 0 if equal, 1 if x has higher cost.</returns>
|
||||
public int Compare(object x, object y)
|
||||
{
|
||||
vx = (Vert)x;
|
||||
vy = (Vert)y;
|
||||
if (vx == vy)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (vx.cost < vy.cost)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1cbe772204b3d5040821696552854402
|
||||
timeCreated: 1493253685
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,76 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
#region
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.MeshDecimation
|
||||
{
|
||||
/// <summary>
|
||||
/// Tracks mesh decimation history for undo/redo operations during progressive mesh simplification.
|
||||
/// </summary>
|
||||
public class History
|
||||
{
|
||||
/// <summary>
|
||||
/// Unique identifier for this history entry.
|
||||
/// </summary>
|
||||
public int id;
|
||||
|
||||
/// <summary>
|
||||
/// List of triangle indices that were removed during this step.
|
||||
/// </summary>
|
||||
public List<int> removedTriangles = new();
|
||||
|
||||
/// <summary>
|
||||
/// List of vertex replacement operations performed during this step.
|
||||
/// </summary>
|
||||
public List<ArrayList> replacedVertex = new();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Records a triangle removal operation.
|
||||
/// </summary>
|
||||
/// <param name="f">Index of the triangle that was removed.</param>
|
||||
public void RemovedTriangle(int f)
|
||||
{
|
||||
removedTriangles.Add(f);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Records a vertex replacement operation including original and new vertex data.
|
||||
/// </summary>
|
||||
/// <param name="f">Face index.</param>
|
||||
/// <param name="u">Original vertex index in the triangle.</param>
|
||||
/// <param name="v">Vertex ID being replaced.</param>
|
||||
/// <param name="normal">Original vertex normal.</param>
|
||||
/// <param name="uv">Original texture coordinates.</param>
|
||||
/// <param name="newV">New vertex ID.</param>
|
||||
/// <param name="newNormal">New vertex normal.</param>
|
||||
/// <param name="newUv">New texture coordinates.</param>
|
||||
public void ReplaceVertex(int f, int u, int v, Vector3 normal, Vector2 uv, int newV, Vector3 newNormal,
|
||||
Vector2 newUv)
|
||||
{
|
||||
ArrayList list = new();
|
||||
list.Insert(0, f);
|
||||
list.Insert(1, u);
|
||||
list.Insert(2, v);
|
||||
list.Insert(3, normal);
|
||||
list.Insert(4, uv);
|
||||
list.Insert(5, newV);
|
||||
list.Insert(6, newNormal);
|
||||
list.Insert(7, newUv);
|
||||
replacedVertex.Add(list);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 19e51ea37ef473d498bef5c1c4f3ebc5
|
||||
timeCreated: 1493253685
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,900 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
#region
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
#endregion
|
||||
|
||||
// Based on:
|
||||
// Progressive Mesh type Polygon Reduction Algorithm
|
||||
// by Stan Melax (c) 1998
|
||||
// http://www.melax.com/polychop/
|
||||
|
||||
namespace NWH.DWP2.MeshDecimation
|
||||
{
|
||||
/// <summary>
|
||||
/// Progressive mesh decimation implementation using edge collapse algorithm.
|
||||
/// Reduces mesh complexity while preserving overall shape and texture coordinates.
|
||||
/// </summary>
|
||||
public class MeshDecimate
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether to recalculate smooth normals after decimation.
|
||||
/// </summary>
|
||||
public bool bRecalculateNormals;
|
||||
|
||||
/// <summary>
|
||||
/// Final vertex normals after decimation.
|
||||
/// </summary>
|
||||
public Vector3[] finalNormals;
|
||||
|
||||
/// <summary>
|
||||
/// Final triangle indices after decimation.
|
||||
/// </summary>
|
||||
public int[] finalTriangles;
|
||||
|
||||
/// <summary>
|
||||
/// Final texture coordinates after decimation.
|
||||
/// </summary>
|
||||
public Vector2[] finalUVs;
|
||||
|
||||
/// <summary>
|
||||
/// Final vertex positions after decimation.
|
||||
/// </summary>
|
||||
public Vector3[] finalVertices;
|
||||
|
||||
/// <summary>
|
||||
/// Last target vertex count from progressive mesh operation.
|
||||
/// </summary>
|
||||
public int lastTarget;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to lock selected vertices from being collapsed.
|
||||
/// </summary>
|
||||
public bool lockSelPoint = true;
|
||||
|
||||
/// <summary>
|
||||
/// Size in bytes of LOD history data for progressive mesh.
|
||||
/// </summary>
|
||||
public float lodDataSize;
|
||||
|
||||
/// <summary>
|
||||
/// Whether pre-calculation of decimation data has been completed.
|
||||
/// </summary>
|
||||
public bool preCalculateDone;
|
||||
|
||||
/// <summary>
|
||||
/// Target ratio of vertices to keep (0.0 to 1.0).
|
||||
/// </summary>
|
||||
public float ratio = 0.5f;
|
||||
|
||||
/// <summary>
|
||||
/// List of vertices marked as selected (protected from collapse if locked).
|
||||
/// </summary>
|
||||
public List<Vector3> selectedVertices = new();
|
||||
|
||||
/// <summary>
|
||||
/// Smoothing angle threshold in degrees for normal calculation.
|
||||
/// </summary>
|
||||
public float smoothAngle = 45.0f;
|
||||
private List<Vert> cache = new();
|
||||
private int cacheSize;
|
||||
private History[] collapseHistory;
|
||||
|
||||
private int currentcnt;
|
||||
private Vert[] myLODVertices;
|
||||
|
||||
private Tri[] myTriangles;
|
||||
private Vector3[] originalNormals;
|
||||
|
||||
private int[] originalTriangles;
|
||||
private Vector2[] originalUVs;
|
||||
private Vector3[] originalVertices;
|
||||
private int searchIndex;
|
||||
|
||||
private int[] sharedTriangles;
|
||||
private Vector3[] sharedVertices;
|
||||
private float smoothAngleDot;
|
||||
private int[] triOrder;
|
||||
|
||||
|
||||
private float ComputeEdgeCollapseCosts(Vert u, Vert v)
|
||||
{
|
||||
int i;
|
||||
int j;
|
||||
Tri faceU;
|
||||
Tri faceV;
|
||||
|
||||
float edgelength = (v.position - u.position).sqrMagnitude;
|
||||
float cost = 0;
|
||||
|
||||
// find the "vFaces" triangles that are on the edge uv
|
||||
List<Tri> vFaces = new();
|
||||
int uFaceCount = u.face.Count;
|
||||
for (i = 0; i < uFaceCount; ++i)
|
||||
{
|
||||
faceU = u.face[i];
|
||||
|
||||
if (faceU.HasVertex(v))
|
||||
{
|
||||
vFaces.Add(faceU);
|
||||
}
|
||||
}
|
||||
|
||||
// use the triangle facing most away from the sides
|
||||
// to determine our curvature term
|
||||
int vFaceCount = vFaces.Count;
|
||||
for (i = 0; i < uFaceCount; ++i)
|
||||
{
|
||||
float mindot = 1; // curve for face i and closer side to it
|
||||
faceU = u.face[i];
|
||||
Vector3 faceN = faceU.normal;
|
||||
for (j = 0; j < vFaceCount; ++j)
|
||||
{
|
||||
// use dot product of face normals. '^' defined in vector
|
||||
faceV = vFaces[j];
|
||||
Vector3 ns = faceV.normal;
|
||||
float dot = (1 - (faceN.x * ns.x + faceN.y * ns.y + faceN.z * ns.z)) * 0.5f;
|
||||
if (dot < mindot)
|
||||
{
|
||||
mindot = dot;
|
||||
}
|
||||
}
|
||||
|
||||
if (mindot > cost)
|
||||
{
|
||||
cost = mindot;
|
||||
}
|
||||
}
|
||||
|
||||
if (u.IsBorder() && vFaceCount > 1)
|
||||
{
|
||||
cost = 1.0f;
|
||||
}
|
||||
|
||||
// texture UV check
|
||||
// if neighbor face has different uv
|
||||
// means that shouldn't be collapsed.
|
||||
// set its priority as higher cost.
|
||||
int found = 0;
|
||||
for (i = 0; i < uFaceCount; ++i)
|
||||
{
|
||||
faceU = u.face[i];
|
||||
Vector2 uv = faceU.uvAt(u);
|
||||
for (j = 0; j < vFaceCount; ++j)
|
||||
{
|
||||
faceV = vFaces[j];
|
||||
if (uv == faceV.uvAt(u))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (j == vFaceCount)
|
||||
{
|
||||
++found;
|
||||
}
|
||||
}
|
||||
|
||||
// all neighbor faces share same uv
|
||||
// so set u as higher cost.
|
||||
if (found > 0)
|
||||
{
|
||||
cost = 1.0f;
|
||||
}
|
||||
|
||||
if (u.selected && lockSelPoint)
|
||||
{
|
||||
cost = 6553.5f;
|
||||
}
|
||||
|
||||
// the more coplanar the lower the curvature term
|
||||
// cost 0 means u and v are on the same plane.
|
||||
return edgelength * cost;
|
||||
}
|
||||
|
||||
|
||||
private void ComputeEdgeCostAtVertex(Vert v)
|
||||
{
|
||||
// compute the edge collapse cost for all edges that start
|
||||
// from vertex v. Since we are only interested in reducing
|
||||
// the object by selecting the min cost edge at each step, we
|
||||
// only cache the cost of the least cost edge at this vertex
|
||||
// (in member variable collapse) as well as the value of the
|
||||
// cost (in member variable cost).
|
||||
if (v.neighbor.Count == 0)
|
||||
{
|
||||
// v doesn't have neighbors so it costs nothing to collapse
|
||||
v.collapse = null;
|
||||
v.cost = 0; //-0.01f;
|
||||
return;
|
||||
}
|
||||
|
||||
v.cost = 65535;
|
||||
v.collapse = null;
|
||||
// search all neighboring edges for "least cost" edge
|
||||
int neighborCount = v.neighbor.Count;
|
||||
float cost;
|
||||
for (int i = 0; i < neighborCount; ++i)
|
||||
{
|
||||
cost = ComputeEdgeCollapseCosts(v, v.neighbor[i]);
|
||||
if (cost < v.cost)
|
||||
{
|
||||
v.collapse = v.neighbor[i]; // candidate for edge collapse
|
||||
v.cost = cost; // cost of the collapse
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void ComputeAllEdgeCollapseCosts()
|
||||
{
|
||||
// For all the edges, compute the difference it would make
|
||||
// to the model if it was collapsed. The least of these
|
||||
// per vertex is cached in each vertex object.
|
||||
int count = myLODVertices.Length;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
Vert v = myLODVertices[i];
|
||||
ComputeEdgeCostAtVertex(v);
|
||||
cache.Insert(i, v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void UnCollapse(History his)
|
||||
{
|
||||
int i;
|
||||
int n;
|
||||
Tri t;
|
||||
|
||||
List<int> l = his.removedTriangles;
|
||||
n = l.Count;
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
myTriangles[l[i]].deleted = false;
|
||||
}
|
||||
|
||||
List<ArrayList> list = his.replacedVertex;
|
||||
n = list.Count;
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
ArrayList tmp = list[i];
|
||||
t = myTriangles[(int)tmp[0]];
|
||||
int changedIndex = (int)tmp[1];
|
||||
|
||||
if (changedIndex == 0)
|
||||
{
|
||||
t.v0 = myLODVertices[(int)tmp[2]];
|
||||
t.vn0 = (Vector3)tmp[3];
|
||||
t.uv0 = (Vector2)tmp[4];
|
||||
}
|
||||
else if (changedIndex == 1)
|
||||
{
|
||||
t.v1 = myLODVertices[(int)tmp[2]];
|
||||
t.vn1 = (Vector3)tmp[3];
|
||||
t.uv1 = (Vector2)tmp[4];
|
||||
}
|
||||
else
|
||||
{
|
||||
t.v2 = myLODVertices[(int)tmp[2]];
|
||||
t.vn2 = (Vector3)tmp[3];
|
||||
t.uv2 = (Vector2)tmp[4];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void Collapse(History his)
|
||||
{
|
||||
int i;
|
||||
int n;
|
||||
Tri t;
|
||||
|
||||
List<int> l = his.removedTriangles;
|
||||
n = l.Count;
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
myTriangles[l[i]].deleted = true;
|
||||
}
|
||||
|
||||
List<ArrayList> list = his.replacedVertex;
|
||||
n = list.Count;
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
ArrayList tmp = list[i];
|
||||
t = myTriangles[(int)tmp[0]];
|
||||
int changedIndex = (int)tmp[1];
|
||||
|
||||
if (changedIndex == 0)
|
||||
{
|
||||
t.v0 = myLODVertices[(int)tmp[5]];
|
||||
t.vn0 = (Vector3)tmp[6];
|
||||
t.uv0 = (Vector2)tmp[7];
|
||||
}
|
||||
else if (changedIndex == 1)
|
||||
{
|
||||
t.v1 = myLODVertices[(int)tmp[5]];
|
||||
t.vn1 = (Vector3)tmp[6];
|
||||
t.uv1 = (Vector2)tmp[7];
|
||||
}
|
||||
else
|
||||
{
|
||||
t.v2 = myLODVertices[(int)tmp[5]];
|
||||
t.vn2 = (Vector3)tmp[6];
|
||||
t.uv2 = (Vector2)tmp[7];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void CollapseTest()
|
||||
{
|
||||
Vert u = cache[searchIndex++];
|
||||
Vert v = u.collapse;
|
||||
|
||||
// which Vert will be collapsed.
|
||||
History his = new();
|
||||
collapseHistory[currentcnt - 1] = his;
|
||||
|
||||
// u is a vertex all by itself so just delete it
|
||||
if (v != null && v.deleted)
|
||||
{
|
||||
u.RemoveVert();
|
||||
return;
|
||||
}
|
||||
|
||||
if (v == null)
|
||||
{
|
||||
u.RemoveVert();
|
||||
return;
|
||||
}
|
||||
|
||||
int i;
|
||||
int j;
|
||||
Tri uFace;
|
||||
Tri vFace;
|
||||
int vFaceCount;
|
||||
int neighborCount = u.neighbor.Count;
|
||||
Vert[] neighbors = new Vert[neighborCount];
|
||||
int count = u.face.Count;
|
||||
|
||||
// make tmp a list of all the neighbors of u
|
||||
for (i = 0; i < neighborCount; ++i)
|
||||
{
|
||||
neighbors[i] = u.neighbor[i];
|
||||
}
|
||||
|
||||
// make a list and add face to the list if it has v.
|
||||
List<Tri> vFaces = new();
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
uFace = u.face[i];
|
||||
if (uFace.HasVertex(v))
|
||||
{
|
||||
vFaces.Add(uFace);
|
||||
}
|
||||
}
|
||||
|
||||
vFaceCount = vFaces.Count;
|
||||
|
||||
// delete triangles on edge uv:
|
||||
for (i = u.face.Count - 1; i >= 0; --i)
|
||||
{
|
||||
try
|
||||
{
|
||||
uFace = u.face[i];
|
||||
if (uFace.HasVertex(v))
|
||||
{
|
||||
uFace.RemoveTriangle(his);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
// update remaining triangles to have v instead of u
|
||||
Vector2 u_uv;
|
||||
Vector2 foundUV = new();
|
||||
Vector3 foundVN = new();
|
||||
|
||||
for (i = u.face.Count - 1; i >= 0; --i)
|
||||
{
|
||||
uFace = u.face[i];
|
||||
if (!uFace.deleted)
|
||||
{
|
||||
u_uv = uFace.uvAt(u);
|
||||
|
||||
for (j = 0; j < vFaceCount; ++j)
|
||||
{
|
||||
vFace = vFaces[j];
|
||||
if (u_uv == vFace.uvAt(u))
|
||||
{
|
||||
foundUV = vFace.uvAt(v);
|
||||
foundVN = vFace.normalAt(v);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uFace.ReplaceVertex(u, v, foundUV, foundVN, his);
|
||||
}
|
||||
}
|
||||
|
||||
u.RemoveVert();
|
||||
|
||||
// recompute the edge collapse costs in neighborhood
|
||||
Vert neighbor;
|
||||
float oldCost;
|
||||
for (i = 0; i < neighborCount; ++i)
|
||||
{
|
||||
neighbor = neighbors[i];
|
||||
oldCost = neighbor.cost;
|
||||
ComputeEdgeCostAtVertex(neighbor);
|
||||
|
||||
if (oldCost > neighbor.cost)
|
||||
{
|
||||
SortLeft(neighbor);
|
||||
}
|
||||
else
|
||||
{
|
||||
SortRight(neighbor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void SortRight(Vert v)
|
||||
{
|
||||
int cacheIndex = cache.IndexOf(v);
|
||||
if (cacheIndex == cacheSize - 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float cost = v.cost;
|
||||
Vert c2 = cache[cacheIndex + 1];
|
||||
if (cost == c2.cost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int maxIndex = cacheSize - 2;
|
||||
while (cost > c2.cost && cacheIndex < maxIndex)
|
||||
{
|
||||
cache[cacheIndex++] = c2;
|
||||
c2 = cache[cacheIndex + 1];
|
||||
}
|
||||
|
||||
if (cost > c2.cost)
|
||||
{
|
||||
cache[cacheIndex++] = c2;
|
||||
}
|
||||
|
||||
cache[cacheIndex] = v;
|
||||
}
|
||||
|
||||
|
||||
private void SortLeft(Vert v)
|
||||
{
|
||||
int cacheIndex = cache.IndexOf(v);
|
||||
if (cacheIndex == searchIndex)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float cost = v.cost;
|
||||
Vert c2 = cache[cacheIndex - 1];
|
||||
if (cost == c2.cost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while (cost < c2.cost && cacheIndex > searchIndex + 2)
|
||||
{
|
||||
cache[cacheIndex--] = c2;
|
||||
c2 = cache[cacheIndex - 1];
|
||||
}
|
||||
|
||||
if (cost < c2.cost)
|
||||
{
|
||||
cache[cacheIndex--] = c2;
|
||||
}
|
||||
|
||||
cache[cacheIndex] = v;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Pre-calculates all edge collapse operations and builds the progressive mesh history.
|
||||
/// Must be called once before Calculate can be used.
|
||||
/// </summary>
|
||||
/// <param name="tmpMesh">Input mesh to decimate.</param>
|
||||
public void PreCalculate(Mesh tmpMesh)
|
||||
{
|
||||
int i;
|
||||
int j;
|
||||
|
||||
smoothAngleDot = 1 - smoothAngle / 90.0f;
|
||||
|
||||
int[] tris = tmpMesh.triangles;
|
||||
|
||||
originalTriangles = tmpMesh.triangles;
|
||||
originalVertices = tmpMesh.vertices;
|
||||
if (tmpMesh.uv.Length > 0)
|
||||
{
|
||||
originalUVs = tmpMesh.uv;
|
||||
}
|
||||
else
|
||||
{
|
||||
List<Vector2> uvs = new();
|
||||
foreach (Vector2 nr in tmpMesh.normals)
|
||||
{
|
||||
uvs.Add(nr);
|
||||
}
|
||||
|
||||
originalUVs = uvs.ToArray();
|
||||
}
|
||||
|
||||
originalNormals = tmpMesh.normals;
|
||||
|
||||
int triNum = tris.Length;
|
||||
int vertNum = originalVertices.Length;
|
||||
List<Vector3> newVertices = new();
|
||||
|
||||
int n;
|
||||
int foundAt = -1;
|
||||
int indice;
|
||||
Vector3 v;
|
||||
|
||||
for (i = 0; i < triNum; ++i)
|
||||
{
|
||||
indice = tris[i];
|
||||
v = originalVertices[indice];
|
||||
|
||||
n = newVertices.Count;
|
||||
foundAt = -1;
|
||||
for (j = 0; j < n; ++j)
|
||||
{
|
||||
if (newVertices[j] == v)
|
||||
{
|
||||
foundAt = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundAt != -1)
|
||||
{
|
||||
tris[i] = foundAt;
|
||||
}
|
||||
else
|
||||
{
|
||||
tris[i] = n;
|
||||
newVertices.Insert(n, v);
|
||||
}
|
||||
}
|
||||
|
||||
sharedTriangles = tris;
|
||||
sharedVertices = newVertices.ToArray();
|
||||
|
||||
myTriangles = new Tri[sharedTriangles.Length / 3];
|
||||
myLODVertices = new Vert[sharedVertices.Length];
|
||||
|
||||
ComputeProgressiveMesh();
|
||||
preCalculateDone = true;
|
||||
|
||||
// calculate triangle remove order
|
||||
triOrder = new int[myTriangles.Length];
|
||||
n = collapseHistory.Length;
|
||||
int cnt = 0;
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
History his = collapseHistory[i];
|
||||
List<int> list = his.removedTriangles;
|
||||
int m = list.Count;
|
||||
for (j = 0; j < m; ++j)
|
||||
{
|
||||
triOrder[cnt++] = list[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the decimated mesh at the current ratio.
|
||||
/// Applies progressive mesh simplification and outputs final geometry arrays.
|
||||
/// PreCalculate must be called first.
|
||||
/// </summary>
|
||||
/// <param name="tmpMesh">Mesh reference (used for validation).</param>
|
||||
public void Calculate(Mesh tmpMesh)
|
||||
{
|
||||
ProgressiveMesh(ratio);
|
||||
|
||||
int i;
|
||||
int j;
|
||||
int foundAt = -1;
|
||||
Vector3 v = new();
|
||||
Vector3 vn = new();
|
||||
Vector3 dvn = new();
|
||||
Vector2 vuv = new();
|
||||
//History his = new History();
|
||||
|
||||
int cnt = 0;
|
||||
int vertsCount = myLODVertices.Length;
|
||||
int trisCount = myTriangles.Length;
|
||||
|
||||
int reducedTriCount = 0;
|
||||
foreach (Tri t in myTriangles)
|
||||
{
|
||||
if (t.deleted)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
++reducedTriCount;
|
||||
}
|
||||
|
||||
int minTriCount = reducedTriCount * 3;
|
||||
int[] tris = new int[minTriCount];
|
||||
Vector3[] verts = new Vector3[minTriCount];
|
||||
Vector2[] uvs = new Vector2[minTriCount];
|
||||
Vector3[] norms = new Vector3[minTriCount];
|
||||
int[] indices = new int[minTriCount];
|
||||
|
||||
for (i = 0; i < reducedTriCount; ++i)
|
||||
{
|
||||
Tri tri = myTriangles[triOrder[i]];
|
||||
|
||||
int cnt1 = cnt + 1;
|
||||
int cnt2 = cnt + 2;
|
||||
Vert v0 = tri.v0;
|
||||
Vert v1 = tri.v1;
|
||||
Vert v2 = tri.v2;
|
||||
|
||||
verts[cnt] = v0.position;
|
||||
verts[cnt1] = v1.position;
|
||||
verts[cnt2] = v2.position;
|
||||
tris[cnt] = cnt;
|
||||
tris[cnt1] = cnt1;
|
||||
tris[cnt2] = cnt2;
|
||||
uvs[cnt] = tri.uv0;
|
||||
uvs[cnt1] = tri.uv1;
|
||||
uvs[cnt2] = tri.uv2;
|
||||
norms[cnt] = tri.vn0;
|
||||
norms[cnt1] = tri.vn1;
|
||||
norms[cnt2] = tri.vn2;
|
||||
|
||||
indices[cnt] = tri.defaultIndex0;
|
||||
indices[cnt1] = tri.defaultIndex1;
|
||||
indices[cnt2] = tri.defaultIndex2;
|
||||
|
||||
cnt += 3;
|
||||
}
|
||||
|
||||
int triNum = tris.Length;
|
||||
List<Vector3> newVertices = new();
|
||||
List<Vector2> newUVs = new();
|
||||
List<Vector3> newNormals = new();
|
||||
List<Vector3> newDNormals = new();
|
||||
|
||||
if (bRecalculateNormals)
|
||||
{
|
||||
for (i = 0; i < triNum; ++i)
|
||||
{
|
||||
v = verts[i];
|
||||
vuv = uvs[i];
|
||||
vn = norms[i];
|
||||
int n = newVertices.Count;
|
||||
foundAt = -1;
|
||||
for (j = 0; j < n; ++j)
|
||||
{
|
||||
if (newVertices[j] == v && newUVs[j] == vuv &&
|
||||
Vector3.Dot(newNormals[j], vn) > smoothAngleDot)
|
||||
{
|
||||
foundAt = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundAt != -1)
|
||||
{
|
||||
tris[i] = foundAt;
|
||||
}
|
||||
else
|
||||
{
|
||||
tris[i] = n;
|
||||
newVertices[n] = v;
|
||||
newUVs[n] = vuv;
|
||||
newNormals[n] = vn;
|
||||
newDNormals[n] = dvn;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < triNum; ++i)
|
||||
{
|
||||
v = verts[i];
|
||||
vuv = uvs[i];
|
||||
vn = norms[i];
|
||||
dvn = originalNormals[indices[i]];
|
||||
int n = newVertices.Count;
|
||||
foundAt = -1;
|
||||
for (j = 0; j < n; ++j)
|
||||
{
|
||||
if (newVertices[j] == v && newUVs[j] == vuv && newDNormals[j] == dvn)
|
||||
{
|
||||
foundAt = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundAt != -1)
|
||||
{
|
||||
tris[i] = foundAt;
|
||||
}
|
||||
else
|
||||
{
|
||||
tris[i] = n;
|
||||
newVertices.Insert(n, v);
|
||||
newUVs.Insert(n, vuv);
|
||||
newNormals.Insert(n, vn);
|
||||
newDNormals.Insert(n, dvn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
finalVertices = newVertices.ToArray();
|
||||
finalNormals = newNormals.ToArray();
|
||||
finalUVs = newUVs.ToArray();
|
||||
finalTriangles = tris;
|
||||
}
|
||||
|
||||
|
||||
private void ComputeProgressiveMesh()
|
||||
{
|
||||
int i;
|
||||
int j;
|
||||
int n;
|
||||
Tri t;
|
||||
|
||||
int vertexCount = sharedVertices.Length;
|
||||
int triangleCount = sharedTriangles.Length;
|
||||
|
||||
Array.Clear(myLODVertices, 0, myLODVertices.Length);
|
||||
for (i = 0; i < vertexCount; ++i)
|
||||
{
|
||||
Vector3 dv = sharedVertices[i];
|
||||
bool sel = false;
|
||||
|
||||
n = selectedVertices.Count;
|
||||
for (j = 0; j < n; ++j)
|
||||
{
|
||||
if (selectedVertices[j] == dv)
|
||||
{
|
||||
sel = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
myLODVertices[i] = new Vert(dv, i, sel);
|
||||
}
|
||||
|
||||
// new myTris
|
||||
Array.Clear(myTriangles, 0, myTriangles.Length);
|
||||
int cnt = 0;
|
||||
for (i = 0; i < triangleCount; i += 3)
|
||||
{
|
||||
t = new Tri(cnt,
|
||||
myLODVertices[sharedTriangles[i]],
|
||||
myLODVertices[sharedTriangles[i + 1]],
|
||||
myLODVertices[sharedTriangles[i + 2]],
|
||||
originalUVs[originalTriangles[i]],
|
||||
originalUVs[originalTriangles[i + 1]],
|
||||
originalUVs[originalTriangles[i + 2]]);
|
||||
|
||||
t.SetDefaultIndices(originalTriangles[i], originalTriangles[i + 1], originalTriangles[i + 2]);
|
||||
if (bRecalculateNormals)
|
||||
{
|
||||
t.vn0 = t.vn1 = t.vn2 = t.normal;
|
||||
}
|
||||
else
|
||||
{
|
||||
t.vn0 = originalNormals[originalTriangles[i]];
|
||||
t.vn1 = originalNormals[originalTriangles[i + 1]];
|
||||
t.vn2 = originalNormals[originalTriangles[i + 2]];
|
||||
}
|
||||
|
||||
myTriangles[cnt] = t;
|
||||
++cnt;
|
||||
}
|
||||
|
||||
cache = new List<Vert>();
|
||||
cacheSize = vertexCount;
|
||||
|
||||
if (bRecalculateNormals)
|
||||
{
|
||||
RecalculateNormal(); // set normals for vertex.
|
||||
}
|
||||
|
||||
ComputeAllEdgeCollapseCosts(); // cache all edge collapse costs
|
||||
//System.Array.Sort(cache, myComparer); // lower cost to the left.
|
||||
cache = cache.OrderBy(p => p.cost).ToList();
|
||||
|
||||
collapseHistory = new History[vertexCount];
|
||||
|
||||
currentcnt = myLODVertices.Length + 1;
|
||||
searchIndex = 0;
|
||||
while (--currentcnt > 0)
|
||||
{
|
||||
CollapseTest();
|
||||
}
|
||||
|
||||
// LOD Data size calculation
|
||||
n = collapseHistory.Length;
|
||||
int tmpBytes = 0;
|
||||
History tmpHis;
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
tmpHis = collapseHistory[i];
|
||||
tmpBytes += (
|
||||
tmpHis.removedTriangles.Count * 2 +
|
||||
tmpHis.replacedVertex.Count * 14
|
||||
) * 4;
|
||||
}
|
||||
|
||||
lodDataSize = tmpBytes;
|
||||
}
|
||||
|
||||
|
||||
private void ProgressiveMesh(float ratio)
|
||||
{
|
||||
int i;
|
||||
int target = Mathf.FloorToInt(ratio * sharedVertices.Length);
|
||||
|
||||
if (lastTarget < target)
|
||||
{
|
||||
for (i = lastTarget; i < target; ++i)
|
||||
{
|
||||
UnCollapse(collapseHistory[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = lastTarget - 1; i >= target; --i)
|
||||
{
|
||||
Collapse(collapseHistory[i]);
|
||||
}
|
||||
}
|
||||
|
||||
lastTarget = target;
|
||||
}
|
||||
|
||||
|
||||
private void RecalculateNormal()
|
||||
{
|
||||
int n = myTriangles.Length;
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
Tri f = myTriangles[i];
|
||||
if (f.deleted)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
f.RecalculateAvgNormals(smoothAngleDot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9ef7548c9b42f29448ab0c06b6c82feb
|
||||
timeCreated: 1493253685
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,447 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
#region
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.MeshDecimation
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a triangle in the mesh decimation algorithm.
|
||||
/// Stores triangle vertices, normals, UVs, and adjacency information.
|
||||
/// </summary>
|
||||
public class Tri
|
||||
{
|
||||
/// <summary>
|
||||
/// Original mesh vertex index for vertex 0.
|
||||
/// </summary>
|
||||
public int defaultIndex0;
|
||||
|
||||
/// <summary>
|
||||
/// Original mesh vertex index for vertex 1.
|
||||
/// </summary>
|
||||
public int defaultIndex1;
|
||||
|
||||
/// <summary>
|
||||
/// Original mesh vertex index for vertex 2.
|
||||
/// </summary>
|
||||
public int defaultIndex2;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this triangle has been deleted.
|
||||
/// </summary>
|
||||
public bool deleted;
|
||||
|
||||
/// <summary>
|
||||
/// Unique identifier for this triangle.
|
||||
/// </summary>
|
||||
public int id;
|
||||
|
||||
/// <summary>
|
||||
/// Face normal vector.
|
||||
/// </summary>
|
||||
public Vector3 normal;
|
||||
|
||||
/// <summary>
|
||||
/// Texture coordinate for vertex 0.
|
||||
/// </summary>
|
||||
public Vector2 uv0;
|
||||
|
||||
/// <summary>
|
||||
/// Texture coordinate for vertex 1.
|
||||
/// </summary>
|
||||
public Vector2 uv1;
|
||||
|
||||
/// <summary>
|
||||
/// Texture coordinate for vertex 2.
|
||||
/// </summary>
|
||||
public Vector2 uv2;
|
||||
|
||||
/// <summary>
|
||||
/// First vertex of the triangle.
|
||||
/// </summary>
|
||||
public Vert v0;
|
||||
|
||||
/// <summary>
|
||||
/// Second vertex of the triangle.
|
||||
/// </summary>
|
||||
public Vert v1;
|
||||
|
||||
/// <summary>
|
||||
/// Third vertex of the triangle.
|
||||
/// </summary>
|
||||
public Vert v2;
|
||||
|
||||
/// <summary>
|
||||
/// Vertex normal for vertex 0.
|
||||
/// </summary>
|
||||
public Vector3 vn0;
|
||||
|
||||
/// <summary>
|
||||
/// Vertex normal for vertex 1.
|
||||
/// </summary>
|
||||
public Vector3 vn1;
|
||||
|
||||
/// <summary>
|
||||
/// Vertex normal for vertex 2.
|
||||
/// </summary>
|
||||
public Vector3 vn2;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new triangle with vertices, texture coordinates, and establishes vertex relationships.
|
||||
/// </summary>
|
||||
/// <param name="id">Unique identifier for this triangle.</param>
|
||||
/// <param name="v0">First vertex.</param>
|
||||
/// <param name="v1">Second vertex.</param>
|
||||
/// <param name="v2">Third vertex.</param>
|
||||
/// <param name="uv0">Texture coordinate for first vertex.</param>
|
||||
/// <param name="uv1">Texture coordinate for second vertex.</param>
|
||||
/// <param name="uv2">Texture coordinate for third vertex.</param>
|
||||
public Tri(int id, Vert v0, Vert v1, Vert v2, Vector2 uv0, Vector2 uv1, Vector2 uv2)
|
||||
{
|
||||
this.id = id;
|
||||
this.v0 = v0;
|
||||
this.v1 = v1;
|
||||
this.v2 = v2;
|
||||
this.uv0 = uv0;
|
||||
this.uv1 = uv1;
|
||||
this.uv2 = uv2;
|
||||
|
||||
RecalculateNormal();
|
||||
|
||||
v0.AddFace(this);
|
||||
v1.AddFace(this);
|
||||
v2.AddFace(this);
|
||||
|
||||
v0.AddNeighbor(v1);
|
||||
v0.AddNeighbor(v2);
|
||||
v1.AddNeighbor(v0);
|
||||
v1.AddNeighbor(v2);
|
||||
v2.AddNeighbor(v0);
|
||||
v2.AddNeighbor(v1);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sets the original mesh vertex indices for this triangle.
|
||||
/// </summary>
|
||||
/// <param name="n0">Original index for vertex 0.</param>
|
||||
/// <param name="n1">Original index for vertex 1.</param>
|
||||
/// <param name="n2">Original index for vertex 2.</param>
|
||||
public void SetDefaultIndices(int n0, int n1, int n2)
|
||||
{
|
||||
defaultIndex0 = n0;
|
||||
defaultIndex1 = n1;
|
||||
defaultIndex2 = n2;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Removes this triangle and updates vertex relationships.
|
||||
/// Records the removal in history for potential undo operations.
|
||||
/// </summary>
|
||||
/// <param name="his">History object to record the removal.</param>
|
||||
public void RemoveTriangle(History his)
|
||||
{
|
||||
v0.RemoveFace(this);
|
||||
v1.RemoveFace(this);
|
||||
v2.RemoveFace(this);
|
||||
|
||||
v0.RemoveIfNonNeighbor(v1);
|
||||
v0.RemoveIfNonNeighbor(v2);
|
||||
v1.RemoveIfNonNeighbor(v0);
|
||||
v1.RemoveIfNonNeighbor(v2);
|
||||
v2.RemoveIfNonNeighbor(v1);
|
||||
v2.RemoveIfNonNeighbor(v0);
|
||||
|
||||
deleted = true;
|
||||
his.RemovedTriangle(id);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the texture coordinate for a given vertex.
|
||||
/// </summary>
|
||||
/// <param name="v">Vertex to query.</param>
|
||||
/// <returns>Texture coordinate for the vertex, or zero vector if not found.</returns>
|
||||
public Vector2 uvAt(Vert v)
|
||||
{
|
||||
Vector3 vec = v.position;
|
||||
if (vec == v0.position)
|
||||
{
|
||||
return uv0;
|
||||
}
|
||||
|
||||
if (vec == v1.position)
|
||||
{
|
||||
return uv1;
|
||||
}
|
||||
|
||||
if (vec == v2.position)
|
||||
{
|
||||
return uv2;
|
||||
}
|
||||
|
||||
return new Vector2();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the vertex normal for a given vertex.
|
||||
/// </summary>
|
||||
/// <param name="v">Vertex to query.</param>
|
||||
/// <returns>Vertex normal for the vertex, or zero vector if not found.</returns>
|
||||
public Vector3 normalAt(Vert v)
|
||||
{
|
||||
Vector3 vec = v.position;
|
||||
if (vec == v0.position)
|
||||
{
|
||||
return vn0;
|
||||
}
|
||||
|
||||
if (vec == v1.position)
|
||||
{
|
||||
return vn1;
|
||||
}
|
||||
|
||||
if (vec == v2.position)
|
||||
{
|
||||
return vn2;
|
||||
}
|
||||
|
||||
return new Vector3();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sets the texture coordinate for a given vertex.
|
||||
/// </summary>
|
||||
/// <param name="v">Vertex to update.</param>
|
||||
/// <param name="newuv">New texture coordinate.</param>
|
||||
public void setUV(Vert v, Vector2 newuv)
|
||||
{
|
||||
Vector3 vec = v.position;
|
||||
if (vec == v0.position)
|
||||
{
|
||||
uv0 = newuv;
|
||||
}
|
||||
else if (vec == v1.position)
|
||||
{
|
||||
uv1 = newuv;
|
||||
}
|
||||
else if (vec == v2.position)
|
||||
{
|
||||
uv2 = newuv;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sets the vertex normal for a given vertex.
|
||||
/// </summary>
|
||||
/// <param name="v">Vertex to update.</param>
|
||||
/// <param name="newNormal">New vertex normal.</param>
|
||||
public void setVN(Vert v, Vector3 newNormal)
|
||||
{
|
||||
Vector3 vec = v.position;
|
||||
if (vec == v0.position)
|
||||
{
|
||||
vn0 = newNormal;
|
||||
}
|
||||
else if (vec == v1.position)
|
||||
{
|
||||
vn1 = newNormal;
|
||||
}
|
||||
else if (vec == v2.position)
|
||||
{
|
||||
vn2 = newNormal;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this triangle contains a given vertex.
|
||||
/// </summary>
|
||||
/// <param name="v">Vertex to check.</param>
|
||||
/// <returns>True if the triangle contains the vertex, false otherwise.</returns>
|
||||
public bool HasVertex(Vert v)
|
||||
{
|
||||
Vector3 vec = v.position;
|
||||
return vec == v0.position || vec == v1.position || vec == v2.position;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Recalculates the face normal using cross product of edge vectors.
|
||||
/// </summary>
|
||||
public void RecalculateNormal()
|
||||
{
|
||||
Vector3 v1pos = v1.position;
|
||||
normal = Vector3.Cross(v1pos - v0.position, v2.position - v1pos);
|
||||
if (normal.magnitude == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
normal.Normalize();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Recalculates averaged vertex normals based on adjacent face normals.
|
||||
/// Only called when normal recalculation is enabled. Smooths normals even at UV seams.
|
||||
/// </summary>
|
||||
/// <param name="smoothAngleDot">Dot product threshold for normal smoothing.</param>
|
||||
public void RecalculateAvgNormals(float smoothAngleDot)
|
||||
{
|
||||
int i;
|
||||
List<Tri> flist = new();
|
||||
List<Tri> slist = new();
|
||||
int n = flist.Count;
|
||||
Tri f;
|
||||
Vector3 fn;
|
||||
|
||||
flist = v0.face;
|
||||
slist.Clear();
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
f = flist[i];
|
||||
fn = f.normal;
|
||||
if (fn.x * normal.x + fn.y * normal.y + fn.z * normal.z > smoothAngleDot)
|
||||
{
|
||||
vn0 += fn;
|
||||
slist.Add(f);
|
||||
}
|
||||
}
|
||||
|
||||
vn0.Normalize();
|
||||
n = slist.Count;
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
f = slist[i];
|
||||
f.setVN(v0, vn0);
|
||||
}
|
||||
|
||||
flist = v1.face;
|
||||
n = flist.Count;
|
||||
slist.Clear();
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
f = flist[i];
|
||||
fn = f.normal;
|
||||
if (fn.x * normal.x + fn.y * normal.y + fn.z * normal.z > smoothAngleDot)
|
||||
{
|
||||
vn1 += fn;
|
||||
slist.Add(f);
|
||||
}
|
||||
}
|
||||
|
||||
vn1.Normalize();
|
||||
n = slist.Count;
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
f = slist[i];
|
||||
f.setVN(v1, vn1);
|
||||
}
|
||||
|
||||
flist = v2.face;
|
||||
n = flist.Count;
|
||||
slist.Clear();
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
f = flist[i];
|
||||
fn = f.normal;
|
||||
if (fn.x * normal.x + fn.y * normal.y + fn.z * normal.z > smoothAngleDot)
|
||||
{
|
||||
vn2 += fn;
|
||||
slist.Add(f);
|
||||
}
|
||||
}
|
||||
|
||||
vn2.Normalize();
|
||||
n = slist.Count;
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
f = slist[i];
|
||||
f.setVN(v2, vn2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Replaces a vertex in this triangle with a new vertex and updates all relationships.
|
||||
/// Records the replacement in history for potential undo operations.
|
||||
/// </summary>
|
||||
/// <param name="vo">Old vertex to be replaced.</param>
|
||||
/// <param name="vnew">New vertex to replace with.</param>
|
||||
/// <param name="newUV">New texture coordinate.</param>
|
||||
/// <param name="newVN">New vertex normal.</param>
|
||||
/// <param name="his">History object to record the replacement.</param>
|
||||
public void ReplaceVertex(Vert vo, Vert vnew, Vector2 newUV, Vector3 newVN, History his)
|
||||
{
|
||||
Vector3 vec = vo.position;
|
||||
Vert changedVertex = v2;
|
||||
int changedVertexId = 2;
|
||||
Vector3 changedNormal = vn2;
|
||||
Vector2 changedUV = uv2;
|
||||
|
||||
if (vec == v0.position)
|
||||
{
|
||||
changedVertex = v0;
|
||||
changedVertexId = 0;
|
||||
changedNormal = vn0;
|
||||
changedUV = uv0;
|
||||
v0 = vnew;
|
||||
vn0 = newVN;
|
||||
uv0 = newUV;
|
||||
}
|
||||
else if (vec == v1.position)
|
||||
{
|
||||
changedVertex = v1;
|
||||
changedVertexId = 1;
|
||||
changedNormal = vn1;
|
||||
changedUV = uv1;
|
||||
v1 = vnew;
|
||||
vn1 = newVN;
|
||||
uv1 = newUV;
|
||||
}
|
||||
else
|
||||
{
|
||||
v2 = vnew;
|
||||
vn2 = newVN;
|
||||
uv2 = newUV;
|
||||
}
|
||||
|
||||
vo.RemoveFace(this);
|
||||
vnew.AddFace(this);
|
||||
|
||||
vo.RemoveIfNonNeighbor(v0);
|
||||
v0.RemoveIfNonNeighbor(vo);
|
||||
vo.RemoveIfNonNeighbor(v1);
|
||||
v1.RemoveIfNonNeighbor(vo);
|
||||
vo.RemoveIfNonNeighbor(v2);
|
||||
v2.RemoveIfNonNeighbor(vo);
|
||||
|
||||
v0.AddNeighbor(v1);
|
||||
v0.AddNeighbor(v2);
|
||||
v1.AddNeighbor(v0);
|
||||
v1.AddNeighbor(v2);
|
||||
v2.AddNeighbor(v0);
|
||||
v2.AddNeighbor(v1);
|
||||
|
||||
RecalculateNormal();
|
||||
|
||||
his.ReplaceVertex(id, changedVertexId, changedVertex.id, changedNormal, changedUV, vnew.id, newVN, newUV);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7f2030e27607db841ac51b6972665e52
|
||||
timeCreated: 1493253685
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,221 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║
|
||||
// ║ Licensed under Unity Asset Store Terms of Service: ║
|
||||
// ║ https://unity.com/legal/as-terms ║
|
||||
// ║ Use permitted only in compliance with the License. ║
|
||||
// ║ Distributed "AS IS", without warranty of any kind. ║
|
||||
// ╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
#region
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.MeshDecimation
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a vertex in the mesh decimation algorithm.
|
||||
/// Stores vertex data and relationships for edge collapse operations.
|
||||
/// </summary>
|
||||
public class Vert
|
||||
{
|
||||
/// <summary>
|
||||
/// Target vertex for edge collapse operation.
|
||||
/// </summary>
|
||||
public Vert collapse;
|
||||
|
||||
/// <summary>
|
||||
/// Edge collapse cost for this vertex.
|
||||
/// </summary>
|
||||
public float cost;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this vertex has been deleted.
|
||||
/// </summary>
|
||||
public bool deleted;
|
||||
|
||||
/// <summary>
|
||||
/// List of triangles that use this vertex.
|
||||
/// </summary>
|
||||
public List<Tri> face = new();
|
||||
|
||||
/// <summary>
|
||||
/// Unique identifier for this vertex.
|
||||
/// </summary>
|
||||
public int id;
|
||||
|
||||
/// <summary>
|
||||
/// List of neighboring vertices connected by edges.
|
||||
/// </summary>
|
||||
public List<Vert> neighbor = new();
|
||||
|
||||
/// <summary>
|
||||
/// 3D position of the vertex.
|
||||
/// </summary>
|
||||
public Vector3 position;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this vertex is marked as selected (prevents collapse if locked).
|
||||
/// </summary>
|
||||
public bool selected;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new vertex with position, ID, and selection status.
|
||||
/// </summary>
|
||||
/// <param name="position">3D position of the vertex.</param>
|
||||
/// <param name="id">Unique identifier.</param>
|
||||
/// <param name="selected">Whether the vertex is selected.</param>
|
||||
public Vert(Vector3 position, int id, bool selected)
|
||||
{
|
||||
this.position = position;
|
||||
this.id = id;
|
||||
this.selected = selected;
|
||||
|
||||
cost = 0f;
|
||||
collapse = null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Removes this vertex and breaks all neighbor connections.
|
||||
/// </summary>
|
||||
public void RemoveVert()
|
||||
{
|
||||
Vert nb;
|
||||
while (neighbor.Count > 0)
|
||||
{
|
||||
nb = neighbor[0];
|
||||
nb.neighbor.Remove(this);
|
||||
neighbor.Remove(nb);
|
||||
}
|
||||
|
||||
deleted = true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this vertex is on the mesh border.
|
||||
/// A vertex is on the border if any of its edges is shared by only one triangle.
|
||||
/// </summary>
|
||||
/// <returns>True if the vertex is on the border, false otherwise.</returns>
|
||||
public bool IsBorder()
|
||||
{
|
||||
int j;
|
||||
int n = neighbor.Count;
|
||||
Vert nb;
|
||||
int face_len;
|
||||
Tri f;
|
||||
int count = 0;
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
count = 0;
|
||||
nb = neighbor[i];
|
||||
face_len = face.Count;
|
||||
for (j = 0; j < face_len; ++j)
|
||||
{
|
||||
f = face[j];
|
||||
if (f.HasVertex(nb))
|
||||
{
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds a triangle to this vertex's face list.
|
||||
/// </summary>
|
||||
/// <param name="f">Triangle to add.</param>
|
||||
public void AddFace(Tri f)
|
||||
{
|
||||
face.Add(f);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Removes a triangle from this vertex's face list.
|
||||
/// </summary>
|
||||
/// <param name="f">Triangle to remove.</param>
|
||||
public void RemoveFace(Tri f)
|
||||
{
|
||||
face.Remove(f);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds a vertex to the neighbor list if not already present.
|
||||
/// </summary>
|
||||
/// <param name="v">Vertex to add as neighbor.</param>
|
||||
public void AddNeighbor(Vert v)
|
||||
{
|
||||
int i;
|
||||
int foundAt = -1;
|
||||
int n = neighbor.Count;
|
||||
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
if (neighbor[i] == v)
|
||||
{
|
||||
foundAt = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundAt == -1)
|
||||
{
|
||||
neighbor.Add(v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Removes a vertex from the neighbor list if they no longer share any faces.
|
||||
/// </summary>
|
||||
/// <param name="v">Vertex to potentially remove from neighbors.</param>
|
||||
public void RemoveIfNonNeighbor(Vert v)
|
||||
{
|
||||
int i;
|
||||
int foundAt = -1;
|
||||
int n = neighbor.Count;
|
||||
Tri f;
|
||||
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
if (neighbor[i] == v)
|
||||
{
|
||||
foundAt = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundAt == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
n = face.Count;
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
f = face[i];
|
||||
if (f.HasVertex(v))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
neighbor.Remove(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b4d9a59a9ba4f0049a08fd05de710c61
|
||||
timeCreated: 1493253685
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user