reworked mesh decimation yes

This commit is contained in:
2026-01-19 09:50:54 +09:00
parent b57d54d69c
commit 54d6a0a1a4
74 changed files with 15788 additions and 8308 deletions

View File

@@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
namespace Nanomesh
{
public static class CollectionUtils
{
public static T[] ToArray<T>(this HashSet<T> items, ref T[] array)
{
int i = 0;
foreach (T item in items)
{
array[i++] = item;
}
return array;
}
public static bool TryAdd<K, V>(this Dictionary<K, V> dictionary, K key, V value)
{
if (dictionary.ContainsKey(key))
{
return false;
}
dictionary.Add(key, value);
return true;
}
public static bool TryAdd<K, V>(this Dictionary<K, V> dictionary, K key, Func<K, V> valueFactory)
{
if (dictionary.ContainsKey(key))
{
return false;
}
dictionary.Add(key, valueFactory(key));
return true;
}
public static V GetOrAdd<K, V>(this Dictionary<K, V> dictionary, K key, V value)
{
if (dictionary.TryGetValue(key, out V existingValue))
{
return existingValue;
}
dictionary.Add(key, value);
return value;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,565 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace Nanomesh
{
public class LinkedHashSet<T> : IReadOnlyCollection<T> where T : IComparable<T>
{
private readonly Dictionary<T, LinkedHashNode<T>> elements;
private LinkedHashNode<T> first, last;
/// <summary>
/// Initializes a new instance of the <see cref="LinkedHashSet{T}"/> class.
/// </summary>
public LinkedHashSet()
{
elements = new Dictionary<T, LinkedHashNode<T>>();
}
/// <summary>
/// Initializes a new instance of the <see cref="LinkedHashSet{T}"/> class.
/// </summary>
/// <param name="initialValues"></param>
public LinkedHashSet(IEnumerable<T> initialValues) : this()
{
UnionWith(initialValues);
}
public LinkedHashNode<T> First => first;
public LinkedHashNode<T> Last => last;
#region Implementation of IEnumerable
/// <summary>
/// Returns an enumerator that iterates through the collection.
/// </summary>
/// <returns>
/// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
/// </returns>
/// <filterpriority>1</filterpriority>
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return GetEnumerator();
}
/// <summary>
/// Returns an enumerator that iterates through a collection.
/// </summary>
/// <returns>
/// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
/// </returns>
/// <filterpriority>2</filterpriority>
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
#region Implementation of ICollection<T>
/// <summary>
/// Gets the number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>.
/// </summary>
/// <returns>
/// The number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>.
/// </returns>
public int Count => elements.Count;
/// <summary>
/// Removes all items from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
/// </summary>
/// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only. </exception>
public void Clear()
{
elements.Clear();
first = null;
last = null;
}
/// <summary>
/// Determines whether the <see cref="T:System.Collections.Generic.ICollection`1"/> contains a specific value.
/// </summary>
/// <returns>
/// true if <paramref name="item"/> is found in the <see cref="T:System.Collections.Generic.ICollection`1"/>; otherwise, false.
/// </returns>
/// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param>
public bool Contains(T item)
{
return elements.ContainsKey(item);
}
/// <summary>
/// Copies the elements of the <see cref="T:System.Collections.Generic.ICollection`1"/> to an <see cref="T:System.Array"/>, starting at a particular <see cref="T:System.Array"/> index.
/// </summary>
/// <param name="array">The one-dimensional <see cref="T:System.Array"/> that is the destination of the elements copied from <see cref="T:System.Collections.Generic.ICollection`1"/>. The <see cref="T:System.Array"/> must have zero-based indexing.</param><param name="arrayIndex">The zero-based index in <paramref name="array"/> at which copying begins.</param><exception cref="T:System.ArgumentNullException"><paramref name="array"/> is null.</exception><exception cref="T:System.ArgumentOutOfRangeException"><paramref name="arrayIndex"/> is less than 0.</exception><exception cref="T:System.ArgumentException"><paramref name="array"/> is multidimensional.-or-The number of elements in the source <see cref="T:System.Collections.Generic.ICollection`1"/> is greater than the available space from <paramref name="arrayIndex"/> to the end of the destination <paramref name="array"/>.-or-Type <typeparamref name="T"/> cannot be cast automatically to the type of the destination <paramref name="array"/>.</exception>
public void CopyTo(T[] array, int arrayIndex)
{
int index = arrayIndex;
foreach (T item in this)
{
array[index++] = item;
}
}
/// <summary>
/// Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
/// </summary>
/// <returns>
/// true if <paramref name="item"/> was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1"/>; otherwise, false. This method also returns false if <paramref name="item"/> is not found in the original <see cref="T:System.Collections.Generic.ICollection`1"/>.
/// </returns>
/// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param><exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.</exception>
public bool Remove(T item)
{
if (elements.TryGetValue(item, out LinkedHashNode<T> node))
{
elements.Remove(item);
Unlink(node);
return true;
}
return false;
}
#endregion
#region Implementation of ISet<T>
/// <summary>
/// Modifies the current set so that it contains all elements that are present in either the current set or the specified collection.
/// </summary>
/// <param name="other">The collection to compare to the current set.</param><exception cref="T:System.ArgumentNullException"><paramref name="other"/> is null.</exception>
public void UnionWith(IEnumerable<T> other)
{
foreach (T item in other)
{
Add(item);
}
}
/// <summary>
/// Modifies the current set so that it contains only elements that are also in a specified collection.
/// </summary>
/// <param name="other">The collection to compare to the current set.</param><exception cref="T:System.ArgumentNullException"><paramref name="other"/> is null.</exception>
public void IntersectWith(IEnumerable<T> other)
{
ISet<T> otherSet = AsSet(other);
LinkedHashNode<T> current = first;
while (current != null)
{
if (!otherSet.Contains(current.Value))
{
elements.Remove(current.Value);
Unlink(current);
}
current = current.Next;
}
}
/// <summary>
/// Removes all elements in the specified collection from the current set.
/// </summary>
/// <param name="other">The collection of items to remove from the set.</param><exception cref="T:System.ArgumentNullException"><paramref name="other"/> is null.</exception>
public void ExceptWith(IEnumerable<T> other)
{
foreach (T item in other)
{
Remove(item);
}
}
/// <summary>
/// Modifies the current set so that it contains only elements that are present either in the current set or in the specified collection, but not both.
/// </summary>
/// <param name="other">The collection to compare to the current set.</param><exception cref="T:System.ArgumentNullException"><paramref name="other"/> is null.</exception>
public void SymmetricExceptWith(IEnumerable<T> other)
{
foreach (T item in other)
{
if (elements.TryGetValue(item, out LinkedHashNode<T> node))
{
elements.Remove(item);
Unlink(node);
}
else
{
Add(item);
}
}
}
/// <summary>
/// Determines whether the current set is a superset of a specified collection.
/// </summary>
/// <returns>
/// true if the current set is a superset of <paramref name="other"/>; otherwise, false.
/// </returns>
/// <param name="other">The collection to compare to the current set.</param><exception cref="T:System.ArgumentNullException"><paramref name="other"/> is null.</exception>
public bool IsSupersetOf(IEnumerable<T> other)
{
int numberOfOthers = CountOthers(other, out int numberOfOthersPresent);
// All others must be present.
return numberOfOthersPresent == numberOfOthers;
}
/// <summary>
/// Determines whether the current set is a correct superset of a specified collection.
/// </summary>
/// <returns>
/// true if the <see cref="T:System.Collections.Generic.ISet`1"/> object is a correct superset of <paramref name="other"/>; otherwise, false.
/// </returns>
/// <param name="other">The collection to compare to the current set. </param><exception cref="T:System.ArgumentNullException"><paramref name="other"/> is null.</exception>
public bool IsProperSupersetOf(IEnumerable<T> other)
{
int numberOfOthers = CountOthers(other, out int numberOfOthersPresent);
// All others must be present, plus we need to have at least one additional item.
return numberOfOthersPresent == numberOfOthers && numberOfOthers < Count;
}
/// <summary>
/// Determines whether the current set and the specified collection contain the same elements.
/// </summary>
/// <returns>
/// true if the current set is equal to <paramref name="other"/>; otherwise, false.
/// </returns>
/// <param name="other">The collection to compare to the current set.</param><exception cref="T:System.ArgumentNullException"><paramref name="other"/> is null.</exception>
public bool SetEquals(IEnumerable<T> other)
{
int numberOfOthers = CountOthers(other, out int numberOfOthersPresent);
return numberOfOthers == Count && numberOfOthersPresent == Count;
}
/// <summary>
/// Adds an element to the current set and returns a value to indicate if the element was successfully added.
/// </summary>
/// <returns>
/// true if the element is added to the set; false if the element is already in the set.
/// </returns>
/// <param name="item">The element to add to the set.</param>
public bool Add(T item)
{
if (elements.ContainsKey(item))
{
return false;
}
LinkedHashNode<T> node = new LinkedHashNode<T>(item) { Previous = last };
if (first == null)
{
first = node;
}
if (last != null)
{
last.Next = node;
}
last = node;
elements.Add(item, node);
return true;
}
public bool AddAfter(T item, LinkedHashNode<T> itemInPlace)
{
if (elements.ContainsKey(item))
{
return false;
}
LinkedHashNode<T> node = new LinkedHashNode<T>(item) { Previous = itemInPlace };
if (itemInPlace.Next != null)
{
node.Next = itemInPlace.Next;
itemInPlace.Next.Previous = node;
}
else
{
last = node;
}
itemInPlace.Next = node;
elements.Add(item, node);
return true;
}
public bool PushAfter(T item, LinkedHashNode<T> itemInPlace)
{
if (elements.ContainsKey(item))
{
return false;
}
LinkedHashNode<T> node = Last;
Unlink(node);
elements.Remove(node.Value);
node.Value = item;
node.Next = null;
node.Previous = itemInPlace;
if (itemInPlace.Next != null)
{
node.Next = itemInPlace.Next;
itemInPlace.Next.Previous = node;
}
else
{
last = node;
}
itemInPlace.Next = node;
elements.Add(item, node);
return true;
}
public bool AddBefore(T item, LinkedHashNode<T> itemInPlace)
{
if (elements.ContainsKey(item))
{
return false;
}
LinkedHashNode<T> node = new LinkedHashNode<T>(item) { Next = itemInPlace };
if (itemInPlace.Previous != null)
{
node.Previous = itemInPlace.Previous;
itemInPlace.Previous.Next = node;
}
else
{
first = node;
}
itemInPlace.Previous = node;
elements.Add(item, node);
return true;
}
public bool PushBefore(T item, LinkedHashNode<T> itemInPlace)
{
if (elements.ContainsKey(item))
{
return false;
}
LinkedHashNode<T> node = Last;
Unlink(node);
elements.Remove(node.Value);
node.Value = item;
node.Previous = null;
node.Next = itemInPlace;
if (itemInPlace.Previous != null)
{
node.Previous = itemInPlace.Previous;
itemInPlace.Previous.Next = node;
}
else
{
first = node;
}
itemInPlace.Previous = node;
elements.Add(item, node);
return true;
}
#endregion
/// <summary>
/// Returns an enumerator that iterates through a collection.
/// </summary>
/// <returns>
/// An <see cref="Enumerator"/> struct that can be used to iterate through the collection.
/// </returns>
public Enumerator GetEnumerator()
{
return new Enumerator(this);
}
/// <summary>
/// Count the elements in the given collection and determine both the total
/// count and how many of the elements that are present in the current set.
/// </summary>
private int CountOthers(IEnumerable<T> items, out int numberOfOthersPresent)
{
numberOfOthersPresent = 0;
int numberOfOthers = 0;
foreach (T item in items)
{
numberOfOthers++;
if (Contains(item))
{
numberOfOthersPresent++;
}
}
return numberOfOthers;
}
/// <summary>
/// Cast the given collection to an ISet&lt;T&gt; if possible. If not,
/// return a new set containing the items.
/// </summary>
private static ISet<T> AsSet(IEnumerable<T> items)
{
return items as ISet<T> ?? new HashSet<T>(items);
}
/// <summary>
/// Unlink a node from the linked list by updating the node pointers in
/// its preceeding and subsequent node. Also update the _first and _last
/// pointers if necessary.
/// </summary>
private void Unlink(LinkedHashNode<T> node)
{
if (node.Previous != null)
{
node.Previous.Next = node.Next;
}
if (node.Next != null)
{
node.Next.Previous = node.Previous;
}
if (ReferenceEquals(node, first))
{
first = node.Next;
}
if (ReferenceEquals(node, last))
{
last = node.Previous;
}
}
public class LinkedHashNode<TElement>
{
public TElement Value;
public LinkedHashNode<TElement> Next;
public LinkedHashNode<TElement> Previous;
public LinkedHashNode(TElement value)
{
Value = value;
}
public override string ToString()
{
return Value.ToString();
}
}
public struct Enumerator : IEnumerator<T>
{
private LinkedHashNode<T> _node;
private T _current;
internal Enumerator(LinkedHashSet<T> set)
{
_current = default(T);
_node = set.first;
}
/// <inheritdoc />
public bool MoveNext()
{
if (_node == null)
{
return false;
}
_current = _node.Value;
_node = _node.Next;
return true;
}
/// <inheritdoc />
public T Current => _current;
/// <inheritdoc />
object IEnumerator.Current => Current;
/// <inheritdoc />
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
/// <inheritdoc />
public void Dispose()
{
}
}
public void AddMin(T item)
{
LinkedHashNode<T> current = Last;
while (current != null && item.CompareTo(current.Value) < 0)
{
current = current.Previous;
}
if (current == Last)
{
return;
}
if (current == null)
{
AddBefore(item, First);
}
else
{
AddAfter(item, current);
}
}
public void PushMin(T item)
{
LinkedHashNode<T> current = Last;
while (current != null && item.CompareTo(current.Value) < 0)
{
current = current.Previous;
}
if (current == Last)
{
return;
}
if (current == null)
{
PushBefore(item, First);
}
else
{
PushAfter(item, current);
}
}
}
}

View File

@@ -0,0 +1,86 @@
using System;
namespace Nanomesh
{
public static class MaxHeap
{
public static T FindKthLargest<T>(T[] nums, int k) where T : IComparable<T>
{
Heap<T> heap = new Heap<T>();
heap.Heapify(nums, nums.Length);
T data = default(T);
for (int i = 0; i < k; i++)
{
data = heap.RemoveMax();
}
return data;
}
}
public class Heap<T> where T : IComparable<T>
{
private T[] arr;
private int count;
private int size;
public int GetLeftChild(int pos)
{
int l = 2 * pos + 1;
return l >= count ? -1 : l;
}
public int GetRightChild(int pos)
{
int r = 2 * pos + 2;
return r >= count ? -1 : r;
}
public void Heapify(T[] num, int n)
{
arr = new T[n];
size = n;
for (int i = 0; i < n; i++)
{
arr[i] = num[i];
}
count = n;
for (int i = (count - 1) / 2; i >= 0; i--)
{
PercolateDown(i);
}
}
public void PercolateDown(int pos)
{
int l = GetLeftChild(pos);
int r = GetRightChild(pos);
int max = pos;
if (l != -1 && arr[max].CompareTo(arr[l]) < 0)
{
max = l;
}
if (r != -1 && arr[max].CompareTo(arr[r]) < 0)
{
max = r;
}
if (max != pos)
{
T temp = arr[pos];
arr[pos] = arr[max];
arr[max] = temp;
PercolateDown(max);
}
}
public T RemoveMax()
{
T data = arr[0];
arr[0] = arr[count - 1];
count--;
PercolateDown(0);
return data;
}
}
}

View File

@@ -0,0 +1,145 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Nanomesh.Collections
{
public class MinHeap<T> : IEnumerable<T>
{
private readonly List<T> values;
private readonly IComparer<T> comparer;
public MinHeap(IEnumerable<T> items, IComparer<T> comparer)
{
values = new List<T>();
this.comparer = comparer;
values.Add(default(T));
values.AddRange(items);
for (int i = values.Count / 2; i >= 1; i--)
{
BubbleDown(i);
}
}
public MinHeap(IEnumerable<T> items) : this(items, Comparer<T>.Default) { }
public MinHeap(IComparer<T> comparer) : this(new T[0], comparer) { }
public MinHeap() : this(Comparer<T>.Default) { }
public int Count => values.Count - 1;
public T Min => values[1];
/// <summary>
/// Extract the smallest element.
/// </summary>
/// <exception cref="InvalidOperationException"></exception>
public T ExtractMin()
{
int count = Count;
if (count == 0)
{
throw new InvalidOperationException("Heap is empty.");
}
T min = Min;
values[1] = values[count];
values.RemoveAt(count);
if (values.Count > 1)
{
BubbleDown(1);
}
return min;
}
/// <summary>
/// Insert the value.
/// </summary>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
public void Add(T item)
{
values.Add(item);
BubbleUp(Count);
}
private void BubbleUp(int index)
{
int parent = index / 2;
while (index > 1 && CompareResult(parent, index) > 0)
{
Exchange(index, parent);
index = parent;
parent /= 2;
}
}
private void BubbleDown(int index)
{
int min;
while (true)
{
int left = index * 2;
int right = index * 2 + 1;
if (left < values.Count &&
CompareResult(left, index) < 0)
{
min = left;
}
else
{
min = index;
}
if (right < values.Count &&
CompareResult(right, min) < 0)
{
min = right;
}
if (min != index)
{
Exchange(index, min);
index = min;
}
else
{
return;
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int CompareResult(int index1, int index2)
{
return comparer.Compare(values[index1], values[index2]);
}
private void Exchange(int index, int max)
{
T tmp = values[index];
values[index] = values[max];
values[max] = tmp;
}
public IEnumerator<T> GetEnumerator()
{
return values.Skip(1).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}

View File

@@ -0,0 +1,118 @@
using System;
namespace Nanomesh
{
public static class OrderStatistics
{
private static T FindMedian<T>(T[] arr, int i, int n)
{
if (i <= n)
{
Array.Sort(arr, i, n); // Sort the array
}
else
{
Array.Sort(arr, n, i);
}
return arr[n / 2]; // Return middle element
}
// Returns k'th smallest element
// in arr[l..r] in worst case
// linear time. ASSUMPTION: ALL
// ELEMENTS IN ARR[] ARE DISTINCT
public static T FindKthSmallest<T>(T[] arr, int l, int r, int k) where T : IComparable<T>
{
// If k is smaller than
// number of elements in array
if (k > 0 && k <= r - l + 1)
{
int n = r - l + 1; // Number of elements in arr[l..r]
// Divide arr[] in groups of size 5,
// calculate median of every group
// and store it in median[] array.
int i;
// There will be floor((n+4)/5) groups;
T[] median = new T[(n + 4) / 5];
for (i = 0; i < n / 5; i++)
{
median[i] = FindMedian(arr, l + i * 5, 5);
}
// For last group with less than 5 elements
if (i * 5 < n)
{
median[i] = FindMedian(arr, l + i * 5, n % 5);
i++;
}
// Find median of all medians using recursive call.
// If median[] has only one element, then no need
// of recursive call
T medOfMed = (i == 1) ? median[i - 1] : FindKthSmallest(median, 0, i - 1, i / 2);
// Partition the array around a random element and
// get position of pivot element in sorted array
int pos = Partition(arr, l, r, medOfMed);
// If position is same as k
if (pos - l == k - 1)
{
return arr[pos];
}
if (pos - l > k - 1) // If position is more, recur for left
{
return FindKthSmallest(arr, l, pos - 1, k);
}
// Else recur for right subarray
return FindKthSmallest(arr, pos + 1, r, k - pos + l - 1);
}
// If k is more than number of elements in array
return default(T);
}
private static void Swap<T>(ref T[] arr, int i, int j)
{
T temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
// It searches for x in arr[l..r], and
// partitions the array around x.
private static int Partition<T>(T[] arr, int l, int r, T x) where T : IComparable<T>
{
// Search for x in arr[l..r] and move it to end
int i;
for (i = l; i < r; i++)
{
if (arr[i].CompareTo(x) == 0)
{
break;
}
}
Swap(ref arr, i, r);
// Standard partition algorithm
i = l;
for (int j = l; j <= r - 1; j++)
{
if (arr[j].CompareTo(x) <= 0)
{
Swap(ref arr, i, j);
i++;
}
}
Swap(ref arr, i, r);
return i;
}
}
}