Added player character and step in/out of dialogue in scene 101

This commit is contained in:
2026-08-15 15:22:47 +02:00
parent a42f25d5fa
commit 38ba2b0eff
2019 changed files with 181227 additions and 265 deletions
@@ -0,0 +1,5 @@
fileFormatVersion: 2
guid: 90dea883dae444c3be7d7720ffcea2b9
folderAsset: yes
DefaultImporter:
userData:
@@ -0,0 +1,4 @@
using System.Runtime.CompilerServices;
[assembly:InternalsVisibleTo("Unity.ProBuilder")]
[assembly:InternalsVisibleTo("Unity.ProBuilder.Editor")]
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 90c015bd1316c423b9aad70704f602ff
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,134 @@
// Original CSG.JS library by Evan Wallace (http://madebyevan.com), under the MIT license.
// GitHub: https://github.com/evanw/csg.js/
//
// C++ port by Tomasz Dabrowski (http://28byteslater.com), under the MIT license.
// GitHub: https://github.com/dabroz/csgjs-cpp/
//
// C# port by Karl Henkel (parabox.co), under MIT license.
//
// Constructive Solid Geometry (CSG) is a modeling technique that uses Boolean
// operations like union and intersection to combine 3D solids. This library
// implements CSG operations on meshes elegantly and concisely using BSP trees,
// and is meant to serve as an easily understandable implementation of the
// algorithm. All edge cases involving overlapping coplanar polygons in both
// solids are correctly handled.
using UnityEngine;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Unity.ProBuilder.Editor.Tests")]
namespace UnityEngine.ProBuilder.Csg
{
/// <summary>
/// Base class for CSG operations. Contains GameObject level methods for Subtraction, Intersection, and Union
/// operations. The GameObjects passed to these functions will not be modified.
/// </summary>
static class CSG
{
public enum BooleanOp
{
Intersection,
Union,
Subtraction
}
const float k_DefaultEpsilon = 0.00001f;
static float s_Epsilon = k_DefaultEpsilon;
/// <summary>
/// Tolerance used by <see cref="Plane.SplitPolygon"/> determine whether planes are coincident.
/// </summary>
public static float epsilon
{
get => s_Epsilon;
set => s_Epsilon = value;
}
#if UNITY_EDITOR
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
static void ResetStaticsOnLoad()
{
s_Epsilon = k_DefaultEpsilon;
}
#endif
/// <summary>
/// Performs a boolean operation on two GameObjects.
/// </summary>
/// <returns>A new mesh.</returns>
public static Model Perform(BooleanOp op, GameObject lhs, GameObject rhs)
{
switch (op)
{
case BooleanOp.Intersection:
return Intersect(lhs, rhs);
case BooleanOp.Union:
return Union(lhs, rhs);
case BooleanOp.Subtraction:
return Subtract(lhs, rhs);
default:
return null;
}
}
/// <summary>
/// Returns a new mesh by merging @lhs with @rhs.
/// </summary>
/// <param name="lhs">The base mesh of the boolean operation.</param>
/// <param name="rhs">The input mesh of the boolean operation.</param>
/// <returns>A new mesh if the operation succeeds, or null if an error occurs.</returns>
public static Model Union(GameObject lhs, GameObject rhs)
{
Model csg_model_a = new Model(lhs);
Model csg_model_b = new Model(rhs);
Node a = new Node(csg_model_a.ToPolygons());
Node b = new Node(csg_model_b.ToPolygons());
List<Polygon> polygons = Node.Union(a, b).AllPolygons();
return new Model(polygons);
}
/// <summary>
/// Returns a new mesh by subtracting @lhs with @rhs.
/// </summary>
/// <param name="lhs">The base mesh of the boolean operation.</param>
/// <param name="rhs">The input mesh of the boolean operation.</param>
/// <returns>A new mesh if the operation succeeds, or null if an error occurs.</returns>
public static Model Subtract(GameObject lhs, GameObject rhs)
{
Model csg_model_a = new Model(lhs);
Model csg_model_b = new Model(rhs);
Node a = new Node(csg_model_a.ToPolygons());
Node b = new Node(csg_model_b.ToPolygons());
List<Polygon> polygons = Node.Subtract(a, b).AllPolygons();
return new Model(polygons);
}
/// <summary>
/// Returns a new mesh by intersecting @lhs with @rhs.
/// </summary>
/// <param name="lhs">The base mesh of the boolean operation.</param>
/// <param name="rhs">The input mesh of the boolean operation.</param>
/// <returns>A new mesh if the operation succeeds, or null if an error occurs.</returns>
public static Model Intersect(GameObject lhs, GameObject rhs)
{
Model csg_model_a = new Model(lhs);
Model csg_model_b = new Model(rhs);
Node a = new Node(csg_model_a.ToPolygons());
Node b = new Node(csg_model_b.ToPolygons());
List<Polygon> polygons = Node.Intersect(a, b).AllPolygons();
return new Model(polygons);
}
}
}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 136c7ff4a0ec419280b8df0d260180d0
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
@@ -0,0 +1,5 @@
fileFormatVersion: 2
guid: 291cedf2747a43b19e37963cf9100589
folderAsset: yes
DefaultImporter:
userData:
@@ -0,0 +1,139 @@
using System;
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
namespace UnityEngine.ProBuilder.Csg
{
/// <summary>
/// Representation of a mesh in CSG terms. Contains methods for translating to and from UnityEngine.Mesh.
/// </summary>
sealed class Model
{
List<Vertex> m_Vertices;
List<Material> m_Materials;
List<List<int>> m_Indices;
public List<Material> materials
{
get { return m_Materials; }
set { m_Materials = value; }
}
public List<Vertex> vertices
{
get { return m_Vertices; }
set { m_Vertices = value; }
}
public List<List<int>> indices
{
get { return m_Indices; }
set { m_Indices = value; }
}
public Mesh mesh
{
get { return (Mesh)this; }
}
public Model(GameObject gameObject) :
this(gameObject.GetComponent<MeshFilter>()?.sharedMesh,
gameObject.GetComponent<MeshRenderer>()?.sharedMaterials,
gameObject.GetComponent<Transform>())
{
}
/// <summary>
/// Initialize a Model from a UnityEngine.Mesh and transform.
/// </summary>
public Model(Mesh mesh, Material[] materials, Transform transform)
{
if(mesh == null)
throw new ArgumentNullException("mesh");
if(transform == null)
throw new ArgumentNullException("transform");
m_Vertices = VertexUtility.GetVertices(mesh).Select(x => transform.TransformVertex(x)).ToList();
m_Materials = new List<Material>(materials);
m_Indices = new List<List<int>>();
for (int i = 0, c = mesh.subMeshCount; i < c; i++)
{
if (mesh.GetTopology(i) != MeshTopology.Triangles)
continue;
var indices = new List<int>();
mesh.GetIndices(indices, i);
m_Indices.Add(indices);
}
}
internal Model(List<Polygon> polygons)
{
m_Vertices = new List<Vertex>();
Dictionary<Material, List<int>> submeshes = new Dictionary<Material, List<int>>();
int p = 0;
for (int i = 0; i < polygons.Count; i++)
{
Polygon poly = polygons[i];
List<int> indices;
if (!submeshes.TryGetValue(poly.material, out indices))
submeshes.Add(poly.material, indices = new List<int>());
for (int j = 2; j < poly.vertices.Count; j++)
{
m_Vertices.Add(poly.vertices[0]);
indices.Add(p++);
m_Vertices.Add(poly.vertices[j - 1]);
indices.Add(p++);
m_Vertices.Add(poly.vertices[j]);
indices.Add(p++);
}
}
m_Materials = submeshes.Keys.ToList();
m_Indices = submeshes.Values.ToList();
}
internal List<Polygon> ToPolygons()
{
List<Polygon> list = new List<Polygon>();
for (int s = 0, c = m_Indices.Count; s < c; s++)
{
var indices = m_Indices[s];
for (int i = 0, ic = indices.Count; i < indices.Count; i += 3)
{
List<Vertex> triangle = new List<Vertex>()
{
m_Vertices[indices[i + 0]],
m_Vertices[indices[i + 1]],
m_Vertices[indices[i + 2]]
};
list.Add(new Polygon(triangle, m_Materials[s]));
}
}
return list;
}
public static explicit operator Mesh(Model model)
{
var mesh = new Mesh();
VertexUtility.SetMesh(mesh, model.m_Vertices);
mesh.subMeshCount = model.m_Indices.Count;
for (int i = 0, c = mesh.subMeshCount; i < c; i++)
mesh.SetIndices(model.m_Indices[i], MeshTopology.Triangles, i);
return mesh;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 14789abc21c8f4551995149f67010e23
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,256 @@
using UnityEngine;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace UnityEngine.ProBuilder.Csg
{
sealed class Node
{
public List<Polygon> polygons;
public Node front;
public Node back;
public Plane plane;
public Node()
{
front = null;
back = null;
}
public Node(List<Polygon> list)
{
Build(list);
}
public Node(List<Polygon> list, Plane plane, Node front, Node back)
{
this.polygons = list;
this.plane = plane;
this.front = front;
this.back = back;
}
public Node Clone()
{
Node clone = new Node(this.polygons, this.plane, this.front, this.back);
return clone;
}
// Remove all polygons in this BSP tree that are inside the other BSP tree
// `bsp`.
public void ClipTo(Node other)
{
this.polygons = other.ClipPolygons(this.polygons);
if (this.front != null)
{
this.front.ClipTo(other);
}
if (this.back != null)
{
this.back.ClipTo(other);
}
}
// Convert solid space to empty space and empty space to solid space.
public void Invert()
{
for (int i = 0; i < this.polygons.Count; i++)
this.polygons[i].Flip();
this.plane.Flip();
if (this.front != null)
{
this.front.Invert();
}
if (this.back != null)
{
this.back.Invert();
}
Node tmp = this.front;
this.front = this.back;
this.back = tmp;
}
// Build a BSP tree out of `polygons`. When called on an existing tree, the
// new polygons are filtered down to the bottom of the tree and become new
// nodes there. Each set of polygons is partitioned using the first polygon
// (no heuristic is used to pick a good split).
public void Build(List<Polygon> list)
{
if (list.Count < 1)
return;
bool newNode = plane == null || !plane.Valid();
if (newNode)
{
plane = new Plane();
plane.normal = list[0].plane.normal;
plane.w = list[0].plane.w;
}
if (polygons == null)
polygons = new List<Polygon>();
var listFront = new List<Polygon>();
var listBack = new List<Polygon>();
for (int i = 0; i < list.Count; i++)
plane.SplitPolygon(list[i], polygons, polygons, listFront, listBack);
if (listFront.Count > 0)
{
// SplitPolygon can fail to correctly identify coplanar planes when the epsilon value is too low. When
// this happens, the front or back list will be filled and built into a new node recursively. This
// check catches that case and sorts the front/back lists into the coplanar polygons collection.
if (newNode && list.SequenceEqual(listFront))
polygons.AddRange(listFront);
else
(front ?? (front = new Node())).Build(listFront);
}
if (listBack.Count > 0)
{
if (newNode && list.SequenceEqual(listBack))
polygons.AddRange(listBack);
else
(back ?? (back = new Node())).Build(listBack);
}
}
// Recursively remove all polygons in `polygons` that are inside this BSP tree.
public List<Polygon> ClipPolygons(List<Polygon> list)
{
if (!this.plane.Valid())
{
return list;
}
List<Polygon> list_front = new List<Polygon>();
List<Polygon> list_back = new List<Polygon>();
for (int i = 0; i < list.Count; i++)
{
this.plane.SplitPolygon(list[i], list_front, list_back, list_front, list_back);
}
if (this.front != null)
{
list_front = this.front.ClipPolygons(list_front);
}
if (this.back != null)
{
list_back = this.back.ClipPolygons(list_back);
}
else
{
list_back.Clear();
}
// Position [First, Last]
// list_front.insert(list_front.end(), list_back.begin(), list_back.end());
list_front.AddRange(list_back);
return list_front;
}
// Return a list of all polygons in this BSP tree.
public List<Polygon> AllPolygons()
{
List<Polygon> list = this.polygons;
List<Polygon> list_front = new List<Polygon>(), list_back = new List<Polygon>();
if (this.front != null)
{
list_front = this.front.AllPolygons();
}
if (this.back != null)
{
list_back = this.back.AllPolygons();
}
list.AddRange(list_front);
list.AddRange(list_back);
return list;
}
#region STATIC OPERATIONS
// Return a new CSG solid representing space in either this solid or in the
// solid `csg`. Neither this solid nor the solid `csg` are modified.
public static Node Union(Node a1, Node b1)
{
Node a = a1.Clone();
Node b = b1.Clone();
a.ClipTo(b);
b.ClipTo(a);
b.Invert();
b.ClipTo(a);
b.Invert();
a.Build(b.AllPolygons());
Node ret = new Node(a.AllPolygons());
return ret;
}
// Return a new CSG solid representing space in this solid but not in the
// solid `csg`. Neither this solid nor the solid `csg` are modified.
public static Node Subtract(Node a1, Node b1)
{
Node a = a1.Clone();
Node b = b1.Clone();
a.Invert();
a.ClipTo(b);
b.ClipTo(a);
b.Invert();
b.ClipTo(a);
b.Invert();
a.Build(b.AllPolygons());
a.Invert();
Node ret = new Node(a.AllPolygons());
return ret;
}
// Return a new CSG solid representing space both this solid and in the
// solid `csg`. Neither this solid nor the solid `csg` are modified.
public static Node Intersect(Node a1, Node b1)
{
Node a = a1.Clone();
Node b = b1.Clone();
a.Invert();
b.ClipTo(a);
b.Invert();
a.ClipTo(b);
b.ClipTo(a);
a.Build(b.AllPolygons());
a.Invert();
Node ret = new Node(a.AllPolygons());
return ret;
}
#endregion
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5b6fe112f5d10423ab960f97d7b6e699
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,156 @@
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
namespace UnityEngine.ProBuilder.Csg
{
/// <summary>
/// Represents a plane in 3d space.
/// <remarks>Does not include position.</remarks>
/// </summary>
sealed class Plane
{
public Vector3 normal;
public float w;
[System.Flags]
enum EPolygonType
{
Coplanar = 0,
Front = 1,
Back = 2,
Spanning = 3 /// 3 is Front | Back - not a separate entry
};
public Plane()
{
normal = Vector3.zero;
w = 0f;
}
public Plane(Vector3 a, Vector3 b, Vector3 c)
{
normal = Vector3.Cross(b - a, c - a);//.normalized;
w = Vector3.Dot(normal, a);
}
public override string ToString() => $"{normal} {w}";
public bool Valid()
{
return normal.magnitude > 0f;
}
public void Flip()
{
normal *= -1f;
w *= -1f;
}
// Split `polygon` by this plane if needed, then put the polygon or polygon
// fragments in the appropriate lists. Coplanar polygons go into either
// `coplanarFront` or `coplanarBack` depending on their orientation with
// respect to this plane. Polygons in front or in back of this plane go into
// either `front` or `back`.
public void SplitPolygon(Polygon polygon, List<Polygon> coplanarFront, List<Polygon> coplanarBack, List<Polygon> front, List<Polygon> back)
{
// Classify each point as well as the entire polygon into one of the above
// four classes.
EPolygonType polygonType = 0;
List<EPolygonType> types = new List<EPolygonType>();
for (int i = 0; i < polygon.vertices.Count; i++)
{
float t = Vector3.Dot(this.normal, polygon.vertices[i].position) - this.w;
EPolygonType type = (t < -CSG.epsilon) ? EPolygonType.Back : ((t > CSG.epsilon) ? EPolygonType.Front : EPolygonType.Coplanar);
polygonType |= type;
types.Add(type);
}
// Put the polygon in the correct list, splitting it when necessary.
switch (polygonType)
{
case EPolygonType.Coplanar:
{
if (Vector3.Dot(this.normal, polygon.plane.normal) > 0)
coplanarFront.Add(polygon);
else
coplanarBack.Add(polygon);
}
break;
case EPolygonType.Front:
{
front.Add(polygon);
}
break;
case EPolygonType.Back:
{
back.Add(polygon);
}
break;
case EPolygonType.Spanning:
{
List<Vertex> f = new List<Vertex>();
List<Vertex> b = new List<Vertex>();
for (int i = 0; i < polygon.vertices.Count; i++)
{
int j = (i + 1) % polygon.vertices.Count;
EPolygonType ti = types[i], tj = types[j];
Vertex vi = polygon.vertices[i], vj = polygon.vertices[j];
if (ti != EPolygonType.Back)
{
f.Add(vi);
}
if (ti != EPolygonType.Front)
{
b.Add(vi);
}
if ((ti | tj) == EPolygonType.Spanning)
{
float t = (this.w - Vector3.Dot(this.normal, vi.position)) / Vector3.Dot(this.normal, vj.position - vi.position);
Vertex v = VertexUtility.Mix(vi, vj, t);
f.Add(v);
b.Add(v);
}
}
if (f.Count >= 3)
{
if (f.SequenceEqual(polygon.vertices))
front.Add(polygon);
else
{
var p = new Polygon(f, polygon.material);
if (p.plane.Valid())
front.Add(p);
}
}
if (b.Count >= 3)
{
if (b.SequenceEqual(polygon.vertices))
back.Add(polygon);
else
{
var p = new Polygon(b, polygon.material);
if (p.plane.Valid())
back.Add(p);
}
}
}
break;
} // End switch(polygonType)
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2665d2fb2b90b4916bfb3868cda99ea3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,37 @@
using UnityEngine;
using System.Collections.Generic;
namespace UnityEngine.ProBuilder.Csg
{
/// <summary>
/// Represents a polygon face with an arbitrary number of vertices.
/// </summary>
sealed class Polygon
{
public List<Vertex> vertices;
public Plane plane;
public Material material;
public Polygon(List<Vertex> list, Material mat)
{
vertices = list;
plane = new Plane(list[0].position, list[1].position, list[2].position);
material = mat;
}
public void Flip()
{
vertices.Reverse();
for (int i = 0; i < vertices.Count; i++)
vertices[i].Flip();
plane.Flip();
}
public override string ToString()
{
return $"[{vertices.Count}] {plane.normal}";
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 52ebe52d390074084bebc0878e3f74b6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,193 @@
using System;
using UnityEngine;
namespace UnityEngine.ProBuilder.Csg
{
/// <summary>
/// Holds information about a single vertex, and provides methods for averaging between many.
/// <remarks>All values are optional. Where not present a default value will be substituted if necessary.</remarks>
/// </summary>
struct Vertex
{
Vector3 m_Position;
Color m_Color;
Vector3 m_Normal;
Vector4 m_Tangent;
Vector2 m_UV0;
Vector2 m_UV2;
Vector4 m_UV3;
Vector4 m_UV4;
VertexAttributes m_Attributes;
/// <value>
/// The position in model space.
/// </value>
public Vector3 position
{
get { return m_Position; }
set
{
hasPosition = true;
m_Position = value;
}
}
/// <value>
/// Vertex color.
/// </value>
public Color color
{
get { return m_Color; }
set
{
hasColor = true;
m_Color = value;
}
}
/// <value>
/// Unit vector normal.
/// </value>
public Vector3 normal
{
get { return m_Normal; }
set
{
hasNormal = true;
m_Normal = value;
}
}
/// <value>
/// Vertex tangent (sometimes called binormal).
/// </value>
public Vector4 tangent
{
get { return m_Tangent; }
set
{
hasTangent = true;
m_Tangent = value;
}
}
/// <value>
/// UV 0 channel. Also called textures.
/// </value>
public Vector2 uv0
{
get { return m_UV0; }
set
{
hasUV0 = true;
m_UV0 = value;
}
}
/// <value>
/// UV 2 channel.
/// </value>
public Vector2 uv2
{
get { return m_UV2; }
set
{
hasUV2 = true;
m_UV2 = value;
}
}
/// <value>
/// UV 3 channel.
/// </value>
public Vector4 uv3
{
get { return m_UV3; }
set
{
hasUV3 = true;
m_UV3 = value;
}
}
/// <value>
/// UV 4 channel.
/// </value>
public Vector4 uv4
{
get { return m_UV4; }
set
{
hasUV4 = true;
m_UV4 = value;
}
}
/// <summary>
/// Find if a vertex attribute has been set.
/// </summary>
/// <param name="attribute">The attribute or attributes to test for.</param>
/// <returns>True if this vertex has the specified attributes set, false if they are default values.</returns>
public bool HasArrays(VertexAttributes attribute)
{
return (m_Attributes & attribute) == attribute;
}
public bool hasPosition
{
get { return (m_Attributes & VertexAttributes.Position) == VertexAttributes.Position; }
private set { m_Attributes = value ? (m_Attributes | VertexAttributes.Position) : (m_Attributes & ~(VertexAttributes.Position)); }
}
public bool hasColor
{
get { return (m_Attributes & VertexAttributes.Color) == VertexAttributes.Color; }
private set { m_Attributes = value ? (m_Attributes | VertexAttributes.Color) : (m_Attributes & ~(VertexAttributes.Color)); }
}
public bool hasNormal
{
get { return (m_Attributes & VertexAttributes.Normal) == VertexAttributes.Normal; }
private set { m_Attributes = value ? (m_Attributes | VertexAttributes.Normal) : (m_Attributes & ~(VertexAttributes.Normal)); }
}
public bool hasTangent
{
get { return (m_Attributes & VertexAttributes.Tangent) == VertexAttributes.Tangent; }
private set { m_Attributes = value ? (m_Attributes | VertexAttributes.Tangent) : (m_Attributes & ~(VertexAttributes.Tangent)); }
}
public bool hasUV0
{
get { return (m_Attributes & VertexAttributes.Texture0) == VertexAttributes.Texture0; }
private set { m_Attributes = value ? (m_Attributes | VertexAttributes.Texture0) : (m_Attributes & ~(VertexAttributes.Texture0)); }
}
public bool hasUV2
{
get { return (m_Attributes & VertexAttributes.Texture1) == VertexAttributes.Texture1; }
private set { m_Attributes = value ? (m_Attributes | VertexAttributes.Texture1) : (m_Attributes & ~(VertexAttributes.Texture1)); }
}
public bool hasUV3
{
get { return (m_Attributes & VertexAttributes.Texture2) == VertexAttributes.Texture2; }
private set { m_Attributes = value ? (m_Attributes | VertexAttributes.Texture2) : (m_Attributes & ~(VertexAttributes.Texture2)); }
}
public bool hasUV4
{
get { return (m_Attributes & VertexAttributes.Texture3) == VertexAttributes.Texture3; }
private set { m_Attributes = value ? (m_Attributes | VertexAttributes.Texture3) : (m_Attributes & ~(VertexAttributes.Texture3)); }
}
public void Flip()
{
if(hasNormal)
m_Normal *= -1f;
if (hasTangent)
m_Tangent *= -1f;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: eace57ffc03f348e48d541e3ac67ae90
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,50 @@
namespace UnityEngine.ProBuilder.Csg
{
/// <summary>
/// Mesh attributes bitmask.
/// </summary>
[System.Flags]
enum VertexAttributes
{
/// <summary>
/// Vertex positions.
/// </summary>
Position = 0x1,
/// <summary>
/// First UV channel.
/// </summary>
Texture0 = 0x2,
/// <summary>
/// Second UV channel. Commonly called UV2 or Lightmap UVs in Unity terms.
/// </summary>
Texture1 = 0x4,
/// <summary>
/// Second UV channel. Commonly called UV2 or Lightmap UVs in Unity terms.
/// </summary>
Lightmap = 0x4,
/// <summary>
/// Third UV channel.
/// </summary>
Texture2 = 0x8,
/// <summary>
/// Vertex UV4.
/// </summary>
Texture3 = 0x10,
/// <summary>
/// Vertex colors.
/// </summary>
Color = 0x20,
/// <summary>
/// Vertex normals.
/// </summary>
Normal = 0x40,
/// <summary>
/// Vertex tangents.
/// </summary>
Tangent = 0x80,
/// <summary>
/// All stored mesh attributes.
/// </summary>
All = 0xFF
};
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7aeb98aaff5054383b10b45f4518f968
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,327 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEngine.ProBuilder.Csg
{
static class VertexUtility
{
/// <summary>
/// Allocate and fill all attribute arrays. This method will fill all arrays, regardless of whether or not real data populates the values (check what attributes a Vertex contains with HasAttribute()).
/// </summary>
/// <remarks>
/// If you are using this function to rebuild a mesh, use SetMesh instead. SetMesh handles setting null arrays where appropriate for you.
/// </remarks>
/// <seealso cref="SetMesh"/>
/// <param name="vertices">The source vertices.</param>
/// <param name="position">A new array of the vertex position values.</param>
/// <param name="color">A new array of the vertex color values.</param>
/// <param name="uv0">A new array of the vertex uv0 values.</param>
/// <param name="normal">A new array of the vertex normal values.</param>
/// <param name="tangent">A new array of the vertex tangent values.</param>
/// <param name="uv2">A new array of the vertex uv2 values.</param>
/// <param name="uv3">A new array of the vertex uv3 values.</param>
/// <param name="uv4">A new array of the vertex uv4 values.</param>
public static void GetArrays(
IList<Vertex> vertices,
out Vector3[] position,
out Color[] color,
out Vector2[] uv0,
out Vector3[] normal,
out Vector4[] tangent,
out Vector2[] uv2,
out List<Vector4> uv3,
out List<Vector4> uv4)
{
GetArrays(vertices, out position, out color, out uv0, out normal, out tangent, out uv2, out uv3, out uv4, VertexAttributes.All);
}
/// <summary>
/// Allocate and fill the requested attribute arrays.
/// </summary>
/// <remarks>
/// If you are using this function to rebuild a mesh, use SetMesh instead. SetMesh handles setting null arrays where appropriate for you.
/// </remarks>
/// <seealso cref="SetMesh"/>
/// <param name="vertices">The source vertices.</param>
/// <param name="position">A new array of the vertex position values if requested by the attributes parameter, or null.</param>
/// <param name="color">A new array of the vertex color values if requested by the attributes parameter, or null.</param>
/// <param name="uv0">A new array of the vertex uv0 values if requested by the attributes parameter, or null.</param>
/// <param name="normal">A new array of the vertex normal values if requested by the attributes parameter, or null.</param>
/// <param name="tangent">A new array of the vertex tangent values if requested by the attributes parameter, or null.</param>
/// <param name="uv2">A new array of the vertex uv2 values if requested by the attributes parameter, or null.</param>
/// <param name="uv3">A new array of the vertex uv3 values if requested by the attributes parameter, or null.</param>
/// <param name="uv4">A new array of the vertex uv4 values if requested by the attributes parameter, or null.</param>
/// <param name="attributes">A flag with the MeshAttributes requested.</param>
/// <seealso cref="HasArrays"/>
public static void GetArrays(
IList<Vertex> vertices,
out Vector3[] position,
out Color[] color,
out Vector2[] uv0,
out Vector3[] normal,
out Vector4[] tangent,
out Vector2[] uv2,
out List<Vector4> uv3,
out List<Vector4> uv4,
VertexAttributes attributes)
{
if (vertices == null)
throw new ArgumentNullException("vertices");
int vc = vertices.Count;
var first = vc < 1 ? new Vertex() : vertices[0];
bool hasPosition = ((attributes & VertexAttributes.Position) == VertexAttributes.Position) && first.hasPosition;
bool hasColor = ((attributes & VertexAttributes.Color) == VertexAttributes.Color) && first.hasColor;
bool hasUv0 = ((attributes & VertexAttributes.Texture0) == VertexAttributes.Texture0) && first.hasUV0;
bool hasNormal = ((attributes & VertexAttributes.Normal) == VertexAttributes.Normal) && first.hasNormal;
bool hasTangent = ((attributes & VertexAttributes.Tangent) == VertexAttributes.Tangent) && first.hasTangent;
bool hasUv2 = ((attributes & VertexAttributes.Texture1) == VertexAttributes.Texture1) && first.hasUV2;
bool hasUv3 = ((attributes & VertexAttributes.Texture2) == VertexAttributes.Texture2) && first.hasUV3;
bool hasUv4 = ((attributes & VertexAttributes.Texture3) == VertexAttributes.Texture3) && first.hasUV4;
position = hasPosition ? new Vector3[vc] : null;
color = hasColor ? new Color[vc] : null;
uv0 = hasUv0 ? new Vector2[vc] : null;
normal = hasNormal ? new Vector3[vc] : null;
tangent = hasTangent ? new Vector4[vc] : null;
uv2 = hasUv2 ? new Vector2[vc] : null;
uv3 = hasUv3 ? new List<Vector4>(vc) : null;
uv4 = hasUv4 ? new List<Vector4>(vc) : null;
for (int i = 0; i < vc; i++)
{
if (hasPosition)
position[i] = vertices[i].position;
if (hasColor)
color[i] = vertices[i].color;
if (hasUv0)
uv0[i] = vertices[i].uv0;
if (hasNormal)
normal[i] = vertices[i].normal;
if (hasTangent)
tangent[i] = vertices[i].tangent;
if (hasUv2)
uv2[i] = vertices[i].uv2;
if (hasUv3)
uv3.Add(vertices[i].uv3);
if (hasUv4)
uv4.Add(vertices[i].uv4);
}
}
public static Vertex[] GetVertices(this Mesh mesh)
{
if (mesh == null)
return null;
int vertexCount = mesh.vertexCount;
Vertex[] v = new Vertex[vertexCount];
Vector3[] positions = mesh.vertices;
Color[] colors = mesh.colors;
Vector3[] normals = mesh.normals;
Vector4[] tangents = mesh.tangents;
Vector2[] uv0s = mesh.uv;
Vector2[] uv2s = mesh.uv2;
List<Vector4> uv3s = new List<Vector4>();
List<Vector4> uv4s = new List<Vector4>();
mesh.GetUVs(2, uv3s);
mesh.GetUVs(3, uv4s);
bool _hasPositions = positions != null && positions.Length == vertexCount;
bool _hasColors = colors != null && colors.Length == vertexCount;
bool _hasNormals = normals != null && normals.Length == vertexCount;
bool _hasTangents = tangents != null && tangents.Length == vertexCount;
bool _hasUv0 = uv0s != null && uv0s.Length == vertexCount;
bool _hasUv2 = uv2s != null && uv2s.Length == vertexCount;
bool _hasUv3 = uv3s.Count == vertexCount;
bool _hasUv4 = uv4s.Count == vertexCount;
for (int i = 0; i < vertexCount; i++)
{
v[i] = new Vertex();
if (_hasPositions)
v[i].position = positions[i];
if (_hasColors)
v[i].color = colors[i];
if (_hasNormals)
v[i].normal = normals[i];
if (_hasTangents)
v[i].tangent = tangents[i];
if (_hasUv0)
v[i].uv0 = uv0s[i];
if (_hasUv2)
v[i].uv2 = uv2s[i];
if (_hasUv3)
v[i].uv3 = uv3s[i];
if (_hasUv4)
v[i].uv4 = uv4s[i];
}
return v;
}
/// <summary>
/// Replace mesh values with vertex array. Mesh is cleared during this function, so be sure to set the triangles after calling.
/// </summary>
/// <param name="mesh">The target mesh.</param>
/// <param name="vertices">The vertices to replace the mesh attributes with.</param>
public static void SetMesh(Mesh mesh, IList<Vertex> vertices)
{
if (mesh == null)
throw new ArgumentNullException("mesh");
if (vertices == null)
throw new ArgumentNullException("vertices");
Vector3[] positions = null;
Color[] colors = null;
Vector2[] uv0s = null;
Vector3[] normals = null;
Vector4[] tangents = null;
Vector2[] uv2s = null;
List<Vector4> uv3s = null;
List<Vector4> uv4s = null;
GetArrays(vertices, out positions,
out colors,
out uv0s,
out normals,
out tangents,
out uv2s,
out uv3s,
out uv4s);
mesh.Clear();
Vertex first = vertices[0];
if (first.hasPosition) mesh.vertices = positions;
if (first.hasColor) mesh.colors = colors;
if (first.hasUV0) mesh.uv = uv0s;
if (first.hasNormal) mesh.normals = normals;
if (first.hasTangent) mesh.tangents = tangents;
if (first.hasUV2) mesh.uv2 = uv2s;
if (first.hasUV3)
if (uv3s != null)
mesh.SetUVs(2, uv3s);
if (first.hasUV4)
if (uv4s != null)
mesh.SetUVs(3, uv4s);
}
/// <summary>
/// Linearly interpolate between two vertices.
/// </summary>
/// <param name="x">Left parameter.</param>
/// <param name="y">Right parameter.</param>
/// <param name="weight">The weight of the interpolation. 0 is fully x, 1 is fully y.</param>
/// <returns>A new vertex interpolated by weight between x and y.</returns>
public static Vertex Mix(this Vertex x, Vertex y, float weight)
{
float i = 1f - weight;
Vertex v = new Vertex();
v.position = x.position * i + y.position * weight;
if (x.hasColor && y.hasColor)
v.color = x.color * i + y.color * weight;
else if (x.hasColor)
v.color = x.color;
else if (y.hasColor)
v.color = y.color;
if (x.hasNormal && y.hasNormal)
v.normal = x.normal * i + y.normal * weight;
else if (x.hasNormal)
v.normal = x.normal;
else if (y.hasNormal)
v.normal = y.normal;
if (x.hasTangent && y.hasTangent)
v.tangent = x.tangent * i + y.tangent * weight;
else if (x.hasTangent)
v.tangent = x.tangent;
else if (y.hasTangent)
v.tangent = y.tangent;
if (x.hasUV0 && y.hasUV0)
v.uv0 = x.uv0 * i + y.uv0 * weight;
else if (x.hasUV0)
v.uv0 = x.uv0;
else if (y.hasUV0)
v.uv0 = y.uv0;
if (x.hasUV2 && y.hasUV2)
v.uv2 = x.uv2 * i + y.uv2 * weight;
else if (x.hasUV2)
v.uv2 = x.uv2;
else if (y.hasUV2)
v.uv2 = y.uv2;
if (x.hasUV3 && y.hasUV3)
v.uv3 = x.uv3 * i + y.uv3 * weight;
else if (x.hasUV3)
v.uv3 = x.uv3;
else if (y.hasUV3)
v.uv3 = y.uv3;
if (x.hasUV4 && y.hasUV4)
v.uv4 = x.uv4 * i + y.uv4 * weight;
else if (x.hasUV4)
v.uv4 = x.uv4;
else if (y.hasUV4)
v.uv4 = y.uv4;
return v;
}
/// <summary>
/// Transform a vertex into world space.
/// </summary>
/// <param name="transform">The transform to apply.</param>
/// <param name="vertex">A model space vertex.</param>
/// <returns>A new vertex in world coordinate space.</returns>
public static Vertex TransformVertex(this Transform transform, Vertex vertex)
{
var v = new Vertex();
if (vertex.HasArrays(VertexAttributes.Position))
v.position = transform.TransformPoint(vertex.position);
if (vertex.HasArrays(VertexAttributes.Color))
v.color = vertex.color;
if (vertex.HasArrays(VertexAttributes.Normal))
v.normal = transform.TransformDirection(vertex.normal);
if (vertex.HasArrays(VertexAttributes.Tangent))
v.tangent = transform.rotation * vertex.tangent;
if (vertex.HasArrays(VertexAttributes.Texture0))
v.uv0 = vertex.uv0;
if (vertex.HasArrays(VertexAttributes.Texture1))
v.uv2 = vertex.uv2;
if (vertex.HasArrays(VertexAttributes.Texture2))
v.uv3 = vertex.uv3;
if (vertex.HasArrays(VertexAttributes.Texture3))
v.uv4 = vertex.uv4;
return v;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a629cb73b331b40449fd411043caf7d3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,12 @@
{
"name": "Unity.ProBuilder.Csg",
"references": [],
"optionalUnityReferences": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": false,
"defineConstraints": []
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 26434c0544da24d83a23c99deaa60965
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7359b607dba860d49ad70ac04d648943
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7f90db759702e5046be95dd90d35ba28
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Unity.ProBuilder")]
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bb4dcd11679b340ee8a7afb51cb0ccfc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,79 @@
namespace UnityEngine.ProBuilder.KdTree
{
struct HyperRect<T>
{
private T[] minPoint;
public T[] MinPoint
{
get
{
return minPoint;
}
set
{
minPoint = new T[value.Length];
value.CopyTo(minPoint, 0);
}
}
private T[] maxPoint;
public T[] MaxPoint
{
get
{
return maxPoint;
}
set
{
maxPoint = new T[value.Length];
value.CopyTo(maxPoint, 0);
}
}
public static HyperRect<T> Infinite(int dimensions, ITypeMath<T> math)
{
var rect = new HyperRect<T>();
rect.MinPoint = new T[dimensions];
rect.MaxPoint = new T[dimensions];
for (var dimension = 0; dimension < dimensions; dimension++)
{
rect.MinPoint[dimension] = math.NegativeInfinity;
rect.MaxPoint[dimension] = math.PositiveInfinity;
}
return rect;
}
public T[] GetClosestPoint(T[] toPoint, ITypeMath<T> math)
{
T[] closest = new T[toPoint.Length];
for (var dimension = 0; dimension < toPoint.Length; dimension++)
{
if (math.Compare(minPoint[dimension], toPoint[dimension]) > 0)
{
closest[dimension] = minPoint[dimension];
}
else if (math.Compare(maxPoint[dimension], toPoint[dimension]) < 0)
{
closest[dimension] = maxPoint[dimension];
}
else
// Point is within rectangle, at least on this dimension
closest[dimension] = toPoint[dimension];
}
return closest;
}
public HyperRect<T> Clone()
{
var rect = new HyperRect<T>();
rect.MinPoint = MinPoint;
rect.MaxPoint = MaxPoint;
return rect;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 518e5a76add435648813be98dd71e2f8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,27 @@
using System.Collections.Generic;
namespace UnityEngine.ProBuilder.KdTree
{
interface IKdTree<TKey, TValue> : IEnumerable<KdTreeNode<TKey, TValue>>
{
bool Add(TKey[] point, TValue value);
bool TryFindValueAt(TKey[] point, out TValue value);
TValue FindValueAt(TKey[] point);
bool TryFindValue(TValue value, out TKey[] point);
TKey[] FindValue(TValue value);
KdTreeNode<TKey, TValue>[] RadialSearch(TKey[] center, TKey radius, int count);
void RemoveAt(TKey[] point);
void Clear();
KdTreeNode<TKey, TValue>[] GetNearestNeighbours(TKey[] point, int count = int.MaxValue);
int Count { get; }
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e8fa221ea996bd7459f869e6d809a95d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,11 @@
namespace UnityEngine.ProBuilder.KdTree
{
interface IPriorityQueue<TItem, TPriority>
{
void Enqueue(TItem item, TPriority priority);
TItem Dequeue();
int Count { get; }
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4c7381a66e715734f82749b2e115722c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,669 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Xml;
namespace UnityEngine.ProBuilder.KdTree
{
enum AddDuplicateBehavior
{
Skip,
Error,
Update,
Collect
}
class DuplicateNodeError : Exception
{
public DuplicateNodeError()
: base("Cannot Add Node With Duplicate Coordinates")
{
}
}
[Serializable]
[DataContract]
class KdTree<TKey, TValue> : IKdTree<TKey, TValue>
{
public KdTree(int dimensions, ITypeMath<TKey> typeMath)
{
this.dimensions = dimensions;
this.typeMath = typeMath;
Count = 0;
}
public KdTree(int dimensions, ITypeMath<TKey> typeMath, AddDuplicateBehavior addDuplicateBehavior)
: this(dimensions, typeMath)
{
AddDuplicateBehavior = addDuplicateBehavior;
}
[DataMember]
private int dimensions;
[DataMember]
private ITypeMath<TKey> typeMath = null;
[DataMember]
private KdTreeNode<TKey, TValue> root = null;
[DataMember]
public AddDuplicateBehavior AddDuplicateBehavior { get; private set; }
public bool Add(TKey[] point, TValue value)
{
var nodeToAdd = new KdTreeNode<TKey, TValue>(point, value);
if (root == null)
{
root = new KdTreeNode<TKey, TValue>(point, value);
}
else
{
int dimension = -1;
KdTreeNode<TKey, TValue> parent = root;
do
{
// Increment the dimension we're searching in
dimension = (dimension + 1) % dimensions;
// Does the node we're adding have the same hyperpoint as this node?
if (typeMath.AreEqual(point, parent.Point))
{
switch (AddDuplicateBehavior)
{
case AddDuplicateBehavior.Skip:
return false;
case AddDuplicateBehavior.Error:
throw new DuplicateNodeError();
case AddDuplicateBehavior.Update:
parent.Value = value;
break;
case AddDuplicateBehavior.Collect:
parent.AddDuplicate(value);
return false;
default:
// Should never happen
throw new Exception("Unexpected AddDuplicateBehavior");
}
}
// Which side does this node sit under in relation to it's parent at this level?
int compare = typeMath.Compare(point[dimension], parent.Point[dimension]);
if (parent[compare] == null)
{
parent[compare] = nodeToAdd;
break;
}
else
{
parent = parent[compare];
}
}
while (true);
}
Count++;
return true;
}
private void ReadChildNodes(KdTreeNode<TKey, TValue> removedNode)
{
if (removedNode.IsLeaf)
return;
// The folllowing code might seem a little redundant but we're using
// 2 queues so we can add the child nodes back in, in (more or less)
// the same order they were added in the first place
var nodesToReadd = new Queue<KdTreeNode<TKey, TValue>>();
var nodesToReaddQueue = new Queue<KdTreeNode<TKey, TValue>>();
if (removedNode.LeftChild != null)
nodesToReaddQueue.Enqueue(removedNode.LeftChild);
if (removedNode.RightChild != null)
nodesToReaddQueue.Enqueue(removedNode.RightChild);
while (nodesToReaddQueue.Count > 0)
{
var nodeToReadd = nodesToReaddQueue.Dequeue();
nodesToReadd.Enqueue(nodeToReadd);
for (int side = -1; side <= 1; side += 2)
{
if (nodeToReadd[side] != null)
{
nodesToReaddQueue.Enqueue(nodeToReadd[side]);
nodeToReadd[side] = null;
}
}
}
while (nodesToReadd.Count > 0)
{
var nodeToReadd = nodesToReadd.Dequeue();
Count--;
Add(nodeToReadd.Point, nodeToReadd.Value);
}
}
public void RemoveAt(TKey[] point)
{
// Is tree empty?
if (root == null)
return;
KdTreeNode<TKey, TValue> node;
if (typeMath.AreEqual(point, root.Point))
{
node = root;
root = null;
Count--;
ReadChildNodes(node);
return;
}
node = root;
int dimension = -1;
do
{
dimension = (dimension + 1) % dimensions;
int compare = typeMath.Compare(point[dimension], node.Point[dimension]);
if (node[compare] == null)
// Can't find node
return;
if (typeMath.AreEqual(point, node[compare].Point))
{
var nodeToRemove = node[compare];
node[compare] = null;
Count--;
ReadChildNodes(nodeToRemove);
}
else
node = node[compare];
}
while (node != null);
}
public KdTreeNode<TKey, TValue>[] GetNearestNeighbours(TKey[] point, int count)
{
if (count > Count)
count = Count;
if (count < 0)
{
throw new ArgumentException("Number of neighbors cannot be negative");
}
if (count == 0)
return new KdTreeNode<TKey, TValue>[0];
var nearestNeighbours = new NearestNeighbourList<KdTreeNode<TKey, TValue>, TKey>(count, typeMath);
var rect = HyperRect<TKey>.Infinite(dimensions, typeMath);
AddNearestNeighbours(root, point, rect, 0, nearestNeighbours, typeMath.MaxValue);
count = nearestNeighbours.Count;
var neighbourArray = new KdTreeNode<TKey, TValue>[count];
for (var index = 0; index < count; index++)
neighbourArray[count - index - 1] = nearestNeighbours.RemoveFurtherest();
return neighbourArray;
}
/*
* 1. Search for the target
*
* 1.1 Start by splitting the specified hyper rect
* on the specified node's point along the current
* dimension so that we end up with 2 sub hyper rects
* (current dimension = depth % dimensions)
*
* 1.2 Check what sub rectangle the the target point resides in
* under the current dimension
*
* 1.3 Set that rect to the nearer rect and also the corresponding
* child node to the nearest rect and node and the other rect
* and child node to the further rect and child node (for use later)
*
* 1.4 Travel into the nearer rect and node by calling function
* recursively with nearer rect and node and incrementing
* the depth
*
* 2. Add leaf to list of nearest neighbours
*
* 3. Walk back up tree and at each level:
*
* 3.1 Add node to nearest neighbours if
* we haven't filled our nearest neighbour
* list yet or if it has a distance to target less
* than any of the distances in our current nearest
* neighbours.
*
* 3.2 If there is any point in the further rectangle that is closer to
* the target than our furtherest nearest neighbour then travel into
* that rect and node
*
* That's it, when it finally finishes traversing the branches
* it needs to we'll have our list!
*/
private void AddNearestNeighbours(
KdTreeNode<TKey, TValue> node,
TKey[] target,
HyperRect<TKey> rect,
int depth,
NearestNeighbourList<KdTreeNode<TKey, TValue>, TKey> nearestNeighbours,
TKey maxSearchRadiusSquared)
{
if (node == null)
return;
// Work out the current dimension
int dimension = depth % dimensions;
// Split our hyper-rect into 2 sub rects along the current
// node's point on the current dimension
var leftRect = rect.Clone();
leftRect.MaxPoint[dimension] = node.Point[dimension];
var rightRect = rect.Clone();
rightRect.MinPoint[dimension] = node.Point[dimension];
// Which side does the target reside in?
int compare = typeMath.Compare(target[dimension], node.Point[dimension]);
var nearerRect = compare <= 0 ? leftRect : rightRect;
var furtherRect = compare <= 0 ? rightRect : leftRect;
var nearerNode = compare <= 0 ? node.LeftChild : node.RightChild;
var furtherNode = compare <= 0 ? node.RightChild : node.LeftChild;
// Let's walk down into the nearer branch
if (nearerNode != null)
{
AddNearestNeighbours(
nearerNode,
target,
nearerRect,
depth + 1,
nearestNeighbours,
maxSearchRadiusSquared);
}
TKey distanceSquaredToTarget;
// Walk down into the further branch but only if our capacity hasn't been reached
// OR if there's a region in the further rect that's closer to the target than our
// current furtherest nearest neighbour
TKey[] closestPointInFurtherRect = furtherRect.GetClosestPoint(target, typeMath);
distanceSquaredToTarget = typeMath.DistanceSquaredBetweenPoints(closestPointInFurtherRect, target);
if (typeMath.Compare(distanceSquaredToTarget, maxSearchRadiusSquared) <= 0)
{
if (nearestNeighbours.IsCapacityReached)
{
if (typeMath.Compare(distanceSquaredToTarget, nearestNeighbours.GetFurtherestDistance()) < 0)
AddNearestNeighbours(
furtherNode,
target,
furtherRect,
depth + 1,
nearestNeighbours,
maxSearchRadiusSquared);
}
else
{
AddNearestNeighbours(
furtherNode,
target,
furtherRect,
depth + 1,
nearestNeighbours,
maxSearchRadiusSquared);
}
}
// Try to add the current node to our nearest neighbours list
distanceSquaredToTarget = typeMath.DistanceSquaredBetweenPoints(node.Point, target);
if (typeMath.Compare(distanceSquaredToTarget, maxSearchRadiusSquared) <= 0)
nearestNeighbours.Add(node, distanceSquaredToTarget);
}
public KdTreeNode<TKey, TValue>[] RadialSearch(TKey[] center, TKey radius, int count)
{
var nearestNeighbours = new NearestNeighbourList<KdTreeNode<TKey, TValue>, TKey>(count, typeMath);
AddNearestNeighbours(
root,
center,
HyperRect<TKey>.Infinite(dimensions, typeMath),
0,
nearestNeighbours,
typeMath.Multiply(radius, radius));
count = nearestNeighbours.Count;
var neighbourArray = new KdTreeNode<TKey, TValue>[count];
for (var index = 0; index < count; index++)
neighbourArray[count - index - 1] = nearestNeighbours.RemoveFurtherest();
return neighbourArray;
}
[DataMember]
public int Count { get; private set; }
public bool TryFindValueAt(TKey[] point, out TValue value)
{
var parent = root;
int dimension = -1;
do
{
if (parent == null)
{
value = default(TValue);
return false;
}
else if (typeMath.AreEqual(point, parent.Point))
{
value = parent.Value;
return true;
}
// Keep searching
dimension = (dimension + 1) % dimensions;
int compare = typeMath.Compare(point[dimension], parent.Point[dimension]);
parent = parent[compare];
}
while (true);
}
public TValue FindValueAt(TKey[] point)
{
TValue value;
if (TryFindValueAt(point, out value))
return value;
else
return default(TValue);
}
public bool TryFindValue(TValue value, out TKey[] point)
{
if (root == null)
{
point = null;
return false;
}
// First-in, First-out list of nodes to search
var nodesToSearch = new Queue<KdTreeNode<TKey, TValue>>();
nodesToSearch.Enqueue(root);
while (nodesToSearch.Count > 0)
{
var nodeToSearch = nodesToSearch.Dequeue();
if (nodeToSearch.Value.Equals(value))
{
point = nodeToSearch.Point;
return true;
}
else
{
for (int side = -1; side <= 1; side += 2)
{
var childNode = nodeToSearch[side];
if (childNode != null)
nodesToSearch.Enqueue(childNode);
}
}
}
point = null;
return false;
}
public TKey[] FindValue(TValue value)
{
TKey[] point;
if (TryFindValue(value, out point))
return point;
else
return null;
}
private void AddNodeToStringBuilder(KdTreeNode<TKey, TValue> node, StringBuilder sb, int depth)
{
sb.AppendLine(node.ToString());
for (var side = -1; side <= 1; side += 2)
{
for (var index = 0; index <= depth; index++)
sb.Append("\t");
sb.Append(side == -1 ? "L " : "R ");
if (node[side] == null)
sb.AppendLine("");
else
AddNodeToStringBuilder(node[side], sb, depth + 1);
}
}
public override string ToString()
{
if (root == null)
return "";
var sb = new StringBuilder();
AddNodeToStringBuilder(root, sb, 0);
return sb.ToString();
}
private void AddNodesToList(KdTreeNode<TKey, TValue> node, List<KdTreeNode<TKey, TValue>> nodes)
{
if (node == null)
return;
nodes.Add(node);
for (var side = -1; side <= 1; side += 2)
{
if (node[side] != null)
{
AddNodesToList(node[side], nodes);
node[side] = null;
}
}
}
private void SortNodesArray(KdTreeNode<TKey, TValue>[] nodes, int byDimension, int fromIndex, int toIndex)
{
for (var index = fromIndex + 1; index <= toIndex; index++)
{
var newIndex = index;
while (true)
{
var a = nodes[newIndex - 1];
var b = nodes[newIndex];
if (typeMath.Compare(b.Point[byDimension], a.Point[byDimension]) < 0)
{
nodes[newIndex - 1] = b;
nodes[newIndex] = a;
}
else
break;
}
}
}
private void AddNodesBalanced(KdTreeNode<TKey, TValue>[] nodes, int byDimension, int fromIndex, int toIndex)
{
if (fromIndex == toIndex)
{
Add(nodes[fromIndex].Point, nodes[fromIndex].Value);
nodes[fromIndex] = null;
return;
}
// Sort the array from the fromIndex to the toIndex
SortNodesArray(nodes, byDimension, fromIndex, toIndex);
// Find the splitting point
int midIndex = fromIndex + (int)System.Math.Round((toIndex + 1 - fromIndex) / 2f) - 1;
// Add the splitting point
Add(nodes[midIndex].Point, nodes[midIndex].Value);
nodes[midIndex] = null;
// Recurse
int nextDimension = (byDimension + 1) % dimensions;
if (fromIndex < midIndex)
AddNodesBalanced(nodes, nextDimension, fromIndex, midIndex - 1);
if (toIndex > midIndex)
AddNodesBalanced(nodes, nextDimension, midIndex + 1, toIndex);
}
public void Balance()
{
var nodeList = new List<KdTreeNode<TKey, TValue>>();
AddNodesToList(root, nodeList);
Clear();
AddNodesBalanced(nodeList.ToArray(), 0, 0, nodeList.Count - 1);
}
private void RemoveChildNodes(KdTreeNode<TKey, TValue> node)
{
for (var side = -1; side <= 1; side += 2)
{
if (node[side] != null)
{
RemoveChildNodes(node[side]);
node[side] = null;
}
}
}
public void Clear()
{
if (root != null)
RemoveChildNodes(root);
}
public void SaveToFile(string filename)
{
var serializer = new DataContractSerializer(typeof(KdTree<TKey, TValue>));
using (FileStream stream = File.Create(filename))
using (var writer = XmlDictionaryWriter.CreateBinaryWriter(stream))
{
serializer.WriteObject(writer, this);
writer.Flush();
}
}
public static KdTree<TKey, TValue> LoadFromFile(string filename)
{
var serializer = new DataContractSerializer(typeof(KdTree<TKey, TValue>));
using (FileStream stream = File.Open(filename, FileMode.Open))
using (var reader = XmlDictionaryReader.CreateBinaryReader(stream, XmlDictionaryReaderQuotas.Max))
{
return (KdTree<TKey, TValue>)serializer.ReadObject(reader);
}
}
public IEnumerator<KdTreeNode<TKey, TValue>> GetEnumerator()
{
var left = new Stack<KdTreeNode<TKey, TValue>>();
var right = new Stack<KdTreeNode<TKey, TValue>>();
Action<KdTreeNode<TKey, TValue>> addLeft = node =>
{
if (node.LeftChild != null)
{
left.Push(node.LeftChild);
}
};
Action<KdTreeNode<TKey, TValue>> addRight = node =>
{
if (node.RightChild != null)
{
right.Push(node.RightChild);
}
};
if (root != null)
{
yield return root;
addLeft(root);
addRight(root);
while (true)
{
if (left.Any())
{
var item = left.Pop();
addLeft(item);
addRight(item);
yield return item;
}
else if (right.Any())
{
var item = right.Pop();
addLeft(item);
addRight(item);
yield return item;
}
else
{
break;
}
}
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 13a653f5b84336c45b2c00b570775a87
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,86 @@
using System;
using System.Text;
using System.Linq;
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace UnityEngine.ProBuilder.KdTree
{
[Serializable]
[DataContract]
class KdTreeNode<TKey, TValue>
{
public KdTreeNode()
{
}
public KdTreeNode(TKey[] point, TValue value)
{
Point = point;
Value = value;
}
[DataMember]
public TKey[] Point;
[DataMember]
public TValue Value = default(TValue);
[DataMember]
public List<TValue> Duplicates = null;
[DataMember]
internal KdTreeNode<TKey, TValue> LeftChild = null;
[DataMember]
internal KdTreeNode<TKey, TValue> RightChild = null;
internal KdTreeNode<TKey, TValue> this[int compare]
{
get
{
if (compare <= 0)
return LeftChild;
else
return RightChild;
}
set
{
if (compare <= 0)
LeftChild = value;
else
RightChild = value;
}
}
public bool IsLeaf
{
get
{
return (LeftChild == null) && (RightChild == null);
}
}
public void AddDuplicate(TValue value)
{
if (Duplicates == null)
Duplicates = new List<TValue>() { value };
else
Duplicates.Add(value);
}
public override string ToString()
{
var sb = new StringBuilder();
for (var dimension = 0; dimension < Point.Length; dimension++)
{
sb.Append(Point[dimension].ToString());
}
if (Value == null)
sb.Append("null");
else
sb.Append(Value.ToString());
return sb.ToString();
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 908a5a36f516c3048abadede5920d5d7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ed05cb2facaa5cc49aab6d1137d15904
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,69 @@
using System;
namespace UnityEngine.ProBuilder.KdTree.Math
{
[Serializable]
class DoubleMath : TypeMath<double>
{
public override int Compare(double a, double b)
{
return a.CompareTo(b);
}
public override bool AreEqual(double a, double b)
{
return a == b;
}
public override double MinValue
{
get { return double.MinValue; }
}
public override double MaxValue
{
get { return double.MaxValue; }
}
public override double Zero
{
get { return 0; }
}
public override double NegativeInfinity { get { return double.NegativeInfinity; } }
public override double PositiveInfinity { get { return double.PositiveInfinity; } }
public override double Add(double a, double b)
{
return a + b;
}
public override double Subtract(double a, double b)
{
return a - b;
}
public override double Multiply(double a, double b)
{
return a * b;
}
public override double DistanceSquaredBetweenPoints(double[] a, double[] b)
{
double distance = Zero;
int dimensions = a.Length;
// Return the absolute distance bewteen 2 hyper points
for (var dimension = 0; dimension < dimensions; dimension++)
{
double distOnThisAxis = Subtract(a[dimension], b[dimension]);
double distOnThisAxisSquared = Multiply(distOnThisAxis, distOnThisAxis);
distance = Add(distance, distOnThisAxisSquared);
}
return distance;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a2c22c8685df2d44b943673a11597711
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,69 @@
using System;
namespace UnityEngine.ProBuilder.KdTree.Math
{
[Serializable]
class FloatMath : TypeMath<float>
{
public override int Compare(float a, float b)
{
return a.CompareTo(b);
}
public override bool AreEqual(float a, float b)
{
return a == b;
}
public override float MinValue
{
get { return float.MinValue; }
}
public override float MaxValue
{
get { return float.MaxValue; }
}
public override float Zero
{
get { return 0; }
}
public override float NegativeInfinity { get { return float.NegativeInfinity; } }
public override float PositiveInfinity { get { return float.PositiveInfinity; } }
public override float Add(float a, float b)
{
return a + b;
}
public override float Subtract(float a, float b)
{
return a - b;
}
public override float Multiply(float a, float b)
{
return a * b;
}
public override float DistanceSquaredBetweenPoints(float[] a, float[] b)
{
float distance = Zero;
int dimensions = a.Length;
// Return the absolute distance bewteen 2 hyper points
for (var dimension = 0; dimension < dimensions; dimension++)
{
float distOnThisAxis = Subtract(a[dimension], b[dimension]);
float distOnThisAxisSquared = Multiply(distOnThisAxis, distOnThisAxis);
distance = Add(distance, distOnThisAxisSquared);
}
return distance;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 483554fdb1324da4ead26f33de2c9990
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,33 @@
namespace UnityEngine.ProBuilder.KdTree
{
interface ITypeMath<T>
{
int Compare(T a, T b);
T MinValue { get; }
T MaxValue { get; }
T Min(T a, T b);
T Max(T a, T b);
bool AreEqual(T a, T b);
bool AreEqual(T[] a, T[] b);
T Add(T a, T b);
T Subtract(T a, T b);
T Multiply(T a, T b);
T Zero { get; }
T NegativeInfinity { get; }
T PositiveInfinity { get; }
T DistanceSquaredBetweenPoints(T[] a, T[] b);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2799d938bc097b0478b150b315487a18
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,64 @@
using System;
namespace UnityEngine.ProBuilder.KdTree.Math
{
[Serializable]
abstract class TypeMath<T> : ITypeMath<T>
{
#region ITypeMath<T> members
public abstract int Compare(T a, T b);
public abstract bool AreEqual(T a, T b);
public virtual bool AreEqual(T[] a, T[] b)
{
if (a.Length != b.Length)
return false;
for (var index = 0; index < a.Length; index++)
{
if (!AreEqual(a[index], b[index]))
return false;
}
return true;
}
public abstract T MinValue { get; }
public abstract T MaxValue { get; }
public T Min(T a, T b)
{
if (Compare(a, b) < 0)
return a;
else
return b;
}
public T Max(T a, T b)
{
if (Compare(a, b) > 0)
return a;
else
return b;
}
public abstract T Zero { get; }
public abstract T NegativeInfinity { get; }
public abstract T PositiveInfinity { get; }
public abstract T Add(T a, T b);
public abstract T Subtract(T a, T b);
public abstract T Multiply(T a, T b);
public abstract T DistanceSquaredBetweenPoints(T[] a, T[] b);
#endregion
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4daa0ba8c6e93d54a9b542d76b235c55
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,83 @@
using System;
namespace UnityEngine.ProBuilder.KdTree
{
interface INearestNeighbourList<TItem, TDistance>
{
bool Add(TItem item, TDistance distance);
TItem GetFurtherest();
TItem RemoveFurtherest();
int MaxCapacity { get; }
int Count { get; }
}
class NearestNeighbourList<TItem, TDistance> : INearestNeighbourList<TItem, TDistance>
{
public NearestNeighbourList(int maxCapacity, ITypeMath<TDistance> distanceMath)
{
this.maxCapacity = maxCapacity;
this.distanceMath = distanceMath;
queue = new PriorityQueue<TItem, TDistance>(maxCapacity, distanceMath);
}
private PriorityQueue<TItem, TDistance> queue;
private ITypeMath<TDistance> distanceMath;
private int maxCapacity;
public int MaxCapacity { get { return maxCapacity; } }
public int Count { get { return queue.Count; } }
public bool Add(TItem item, TDistance distance)
{
if (queue.Count >= maxCapacity)
{
// If the distance of this item is less than the distance of the last item
// in our neighbour list then pop that neighbour off and push this one on
// otherwise don't even bother adding this item
if (distanceMath.Compare(distance, queue.GetHighestPriority()) < 0)
{
queue.Dequeue();
queue.Enqueue(item, distance);
return true;
}
else
return false;
}
else
{
queue.Enqueue(item, distance);
return true;
}
}
public TItem GetFurtherest()
{
if (Count == 0)
throw new Exception("List is empty");
else
return queue.GetHighest();
}
public TDistance GetFurtherestDistance()
{
if (Count == 0)
throw new Exception("List is empty");
else
return queue.GetHighestPriority();
}
public TItem RemoveFurtherest()
{
return queue.Dequeue();
}
public bool IsCapacityReached
{
get { return Count == MaxCapacity; }
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 601f62929862ecb48956dc2dca0a2498
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,125 @@
using System;
namespace UnityEngine.ProBuilder.KdTree
{
struct ItemPriority<TItem, TPriority>
{
public TItem Item;
public TPriority Priority;
}
class PriorityQueue<TItem, TPriority> : IPriorityQueue<TItem, TPriority>
{
public PriorityQueue(int capacity, ITypeMath<TPriority> priorityMath)
{
if (capacity <= 0)
throw new ArgumentException("Capacity must be greater than zero");
this.capacity = capacity;
queue = new ItemPriority<TItem, TPriority>[capacity];
this.priorityMath = priorityMath;
}
private ITypeMath<TPriority> priorityMath;
private ItemPriority<TItem, TPriority>[] queue;
private int capacity;
private int count;
public int Count { get { return count; } }
// Try to avoid unnecessary slow memory reallocations by creating your queue with an ample capacity
private void ExpandCapacity()
{
// Double our capacity
capacity *= 2;
// Create a new queue
var newQueue = new ItemPriority<TItem, TPriority>[capacity];
// Copy the contents of the original queue to the new one
Array.Copy(queue, newQueue, queue.Length);
// Copy the new queue over the original one
queue = newQueue;
}
public void Enqueue(TItem item, TPriority priority)
{
if (++count > capacity)
ExpandCapacity();
int newItemIndex = count - 1;
queue[newItemIndex] = new ItemPriority<TItem, TPriority> { Item = item, Priority = priority };
ReorderItem(newItemIndex, -1);
}
public TItem Dequeue()
{
TItem item = queue[0].Item;
queue[0].Item = default(TItem);
queue[0].Priority = priorityMath.MinValue;
ReorderItem(0, 1);
count--;
return item;
}
private void ReorderItem(int index, int direction)
{
if ((direction != -1) && (direction != 1))
throw new ArgumentException("Invalid Direction");
var item = queue[index];
int nextIndex = index + direction;
while ((nextIndex >= 0) && (nextIndex < count))
{
var next = queue[nextIndex];
int compare = priorityMath.Compare(item.Priority, next.Priority);
// If we're moving up and our priority is higher than the next priority then swap
// Or if we're moving down and our priority is lower than the next priority then swap
if (
((direction == -1) && (compare > 0))
||
((direction == 1) && (compare < 0))
)
{
queue[index] = next;
queue[nextIndex] = item;
index += direction;
nextIndex += direction;
}
else
break;
}
}
public TItem GetHighest()
{
if (count == 0)
throw new Exception("Queue is empty");
else
return queue[0].Item;
}
public TPriority GetHighestPriority()
{
if (count == 0)
throw new Exception("Queue is empty");
else
return queue[0].Priority;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d9ed04c4f3db58d488f75533eee62283
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2013 codeandcats
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,7 @@
fileFormatVersion: 2
guid: 16add6facd81da34caaa1fc21522be87
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,11 @@
KdTree
======
A fast, generic, multi-dimensional Binary Search Tree written in C#
Forked from [codeandcats KdTree](https://github.com/codeandcats/KdTree).
## Changes from KdTree
This branch is modified to compile for .NET Framework 3.5, and removes the non-MIT licensed GeoUtils class.
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 348af47767ff3b44cbd7a045bba8a65c
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,12 @@
{
"name": "Unity.ProBuilder.KdTree",
"references": [],
"optionalUnityReferences": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": false,
"defineConstraints": []
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 1c49da3d3671a1748aea5347c1176d2e
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 49b84cc0fd35240458328f98dd8b6d1a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Unity.ProBuilder")]
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e6d7500d036914f3b9768689959295b4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,112 @@
/* Poly2Tri
* Copyright (c) 2009-2010, Poly2Tri Contributors
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace UnityEngine.ProBuilder.Poly2Tri {
static class P2T {
private static TriangulationAlgorithm _defaultAlgorithm = TriangulationAlgorithm.DTSweep;
#if UNITY_EDITOR
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
static void ResetStaticsOnLoad()
{
_defaultAlgorithm = TriangulationAlgorithm.DTSweep;
}
#endif
public static void Triangulate(PolygonSet ps)
{
TriangulationContext tcx = CreateContext(_defaultAlgorithm);
foreach (Polygon p in ps.Polygons)
{
tcx.PrepareTriangulation(p);
Triangulate(tcx);
tcx.Clear();
}
}
public static void Triangulate(Polygon p) {
Triangulate(_defaultAlgorithm, p);
}
public static void Triangulate(ConstrainedPointSet cps) {
Triangulate(_defaultAlgorithm, cps);
}
public static void Triangulate(PointSet ps) {
Triangulate(_defaultAlgorithm, ps);
}
public static TriangulationContext CreateContext(TriangulationAlgorithm algorithm) {
switch (algorithm) {
case TriangulationAlgorithm.DTSweep:
default:
return new DTSweepContext();
}
}
public static void Triangulate(TriangulationAlgorithm algorithm, Triangulatable t) {
TriangulationContext tcx;
// long time = System.nanoTime();
tcx = CreateContext(algorithm);
tcx.PrepareTriangulation(t);
Triangulate(tcx);
// logger.info( "Triangulation of {} points [{}ms]", tcx.getPoints().size(), ( System.nanoTime() - time ) / 1e6 );
}
public static void Triangulate(TriangulationContext tcx) {
switch (tcx.Algorithm) {
case TriangulationAlgorithm.DTSweep:
default:
DTSweep.Triangulate((DTSweepContext)tcx);
break;
}
}
/// <summary>
/// Will do a warmup run to let the JVM optimize the triangulation code -- or would if this were Java --MM
/// </summary>
public static void Warmup() {
#if false
/*
* After a method is run 10000 times, the Hotspot compiler will compile
* it into native code. Periodically, the Hotspot compiler may recompile
* the method. After an unspecified amount of time, then the compilation
* system should become quiet.
*/
Polygon poly = PolygonGenerator.RandomCircleSweep2(50, 50000);
TriangulationProcess process = new TriangulationProcess();
process.triangulate(poly);
#endif
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 09410b7978d32924c90d84fa25a07f4a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 53da0e7aea43ea649a18555f801ee05e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,214 @@
/* Poly2Tri
* Copyright (c) 2009-2010, Poly2Tri Contributors
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/// Changes from the Java version
/// Polygon constructors sprused up, checks for 3+ polys
/// Naming of everything
/// getTriangulationMode() -> TriangulationMode { get; }
/// Exceptions replaced
/// Future possibilities
/// We have a lot of Add/Clear methods -- we may prefer to just expose the container
/// Some self-explanitory methods may deserve commenting anyways
using System;
using System.Collections.Generic;
using System.Linq;
namespace UnityEngine.ProBuilder.Poly2Tri {
class Polygon : Triangulatable {
protected List<TriangulationPoint> _points = new List<TriangulationPoint>();
protected List<TriangulationPoint> _steinerPoints;
protected List<Polygon> _holes;
protected List<DelaunayTriangle> _triangles;
protected PolygonPoint _last;
/// <summary>
/// Create a polygon from a list of at least 3 points with no duplicates.
/// </summary>
/// <param name="points">A list of unique points</param>
public Polygon( IList<PolygonPoint> points ) {
if (points.Count < 3) throw new ArgumentException("List has fewer than 3 points", "points");
// Lets do one sanity check that first and last point hasn't got same position
// Its something that often happen when importing polygon data from other formats
if (points[0].Equals(points[points.Count - 1])) points.RemoveAt(points.Count - 1);
_points.AddRange(points.Cast<TriangulationPoint>());
}
/// <summary>
/// Create a polygon from a list of at least 3 points with no duplicates.
/// </summary>
/// <param name="points">A list of unique points.</param>
public Polygon( IEnumerable<PolygonPoint> points ): this( (points as IList<PolygonPoint>) ?? points.ToArray() ) {}
/// <summary>
/// Create a polygon from a list of at least 3 points with no duplicates.
/// </summary>
/// <param name="points">A list of unique points.</param>
public Polygon( params PolygonPoint[] points ) : this((IList<PolygonPoint>)points) { }
public TriangulationMode TriangulationMode { get { return TriangulationMode.Polygon; } }
public void AddSteinerPoint( TriangulationPoint point ) {
if (_steinerPoints == null) _steinerPoints = new List<TriangulationPoint>();
_steinerPoints.Add(point);
}
public void AddSteinerPoints( List<TriangulationPoint> points ) {
if (_steinerPoints == null) _steinerPoints = new List<TriangulationPoint>();
_steinerPoints.AddRange(points);
}
public void ClearSteinerPoints() {
if (_steinerPoints != null) _steinerPoints.Clear();
}
/// <summary>
/// Add a hole to the polygon.
/// </summary>
/// <param name="poly">A subtraction polygon fully contained inside this polygon.</param>
public void AddHole( Polygon poly ) {
if (_holes == null) _holes = new List<Polygon>();
_holes.Add(poly);
// XXX: tests could be made here to be sure it is fully inside
// addSubtraction( poly.getPoints() );
}
/// <summary>
/// Inserts newPoint after point.
/// </summary>
/// <param name="point">The point to insert after in the polygon</param>
/// <param name="newPoint">The point to insert into the polygon</param>
public void InsertPointAfter( PolygonPoint point, PolygonPoint newPoint ) {
// Validate that
int index = _points.IndexOf(point);
if (index == -1) throw new ArgumentException("Tried to insert a point into a Polygon after a point not belonging to the Polygon", "point");
newPoint.Next = point.Next;
newPoint.Previous = point;
point.Next.Previous = newPoint;
point.Next = newPoint;
_points.Insert(index + 1, newPoint);
}
/// <summary>
/// Inserts list (after last point in polygon?)
/// </summary>
/// <param name="list"></param>
public void AddPoints( IEnumerable<PolygonPoint> list ) {
PolygonPoint first;
foreach (PolygonPoint p in list) {
p.Previous = _last;
if (_last != null) {
p.Next = _last.Next;
_last.Next = p;
}
_last = p;
_points.Add(p);
}
first = (PolygonPoint)_points[0];
_last.Next = first;
first.Previous = _last;
}
/// <summary>
/// Adds a point after the last in the polygon.
/// </summary>
/// <param name="p">The point to add</param>
public void AddPoint( PolygonPoint p ) {
p.Previous = _last;
p.Next = _last.Next;
_last.Next = p;
_points.Add(p);
}
/// <summary>
/// Removes a point from the polygon.
/// </summary>
/// <param name="p"></param>
public void RemovePoint( PolygonPoint p ) {
PolygonPoint next, prev;
next = p.Next;
prev = p.Previous;
prev.Next = next;
next.Previous = prev;
_points.Remove(p);
}
public IList<TriangulationPoint> Points { get { return _points; } }
public IList<DelaunayTriangle> Triangles { get { return _triangles; } }
public IList<Polygon> Holes { get { return _holes; }}
public void AddTriangle( DelaunayTriangle t ) {
_triangles.Add(t);
}
public void AddTriangles( IEnumerable<DelaunayTriangle> list ) {
_triangles.AddRange(list);
}
public void ClearTriangles() {
if (_triangles != null) _triangles.Clear();
}
/// <summary>
/// Creates constraints and populates the context with points
/// </summary>
/// <param name="tcx">The context</param>
public void Prepare( TriangulationContext tcx ) {
if (_triangles == null) {
_triangles = new List<DelaunayTriangle>(_points.Count);
} else {
_triangles.Clear();
}
// Outer constraints
for (int i = 0; i < _points.Count - 1; i++) tcx.NewConstraint(_points[i], _points[i + 1]);
tcx.NewConstraint(_points[0], _points[_points.Count - 1]);
tcx.Points.AddRange(_points);
// Hole constraints
if (_holes != null) {
foreach (Polygon p in _holes) {
for (int i = 0; i < p._points.Count - 1; i++) tcx.NewConstraint(p._points[i], p._points[i + 1]);
tcx.NewConstraint(p._points[0], p._points[p._points.Count - 1]);
tcx.Points.AddRange(p._points);
}
}
if (_steinerPoints != null) {
tcx.Points.AddRange(_steinerPoints);
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d563a6b12a743cf4399b92cf5366da6f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,44 @@
/* Poly2Tri
* Copyright (c) 2009-2010, Poly2Tri Contributors
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/// Changes from the Java version
/// Replaced get/set Next/Previous with attributes
/// Future possibilities
/// Documentation!
namespace UnityEngine.ProBuilder.Poly2Tri {
class PolygonPoint : TriangulationPoint {
public PolygonPoint( double x, double y, int index = INSERTED_INDEX ) : base(x, y, index) { }
public PolygonPoint Next { get; set; }
public PolygonPoint Previous { get; set; }
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a34d12b3d3eeda641bac82627738a726
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,56 @@
/* Poly2Tri
* Copyright (c) 2009-2010, Poly2Tri Contributors
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/// Changes from the Java version
/// Replaced getPolygons with attribute
/// Future possibilities
/// Replace Add(Polygon) with exposed container?
/// Replace entire class with HashSet<Polygon> ?
using System.Collections.Generic;
namespace UnityEngine.ProBuilder.Poly2Tri {
class PolygonSet {
protected List<Polygon> _polygons = new List<Polygon>();
public PolygonSet() {}
public PolygonSet(Polygon poly) {
_polygons.Add(poly);
}
public void Add(Polygon p) {
_polygons.Add(p);
}
public IEnumerable<Polygon> Polygons { get { return _polygons; } }
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3a73369e033539a42ae8067a8fa967c0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a3f53fade6ed6c14aad5577862937e1c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d880a3d3b580e384db43a01ff5d06326
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,215 @@
/* Poly2Tri
* Copyright (c) 2009-2010, Poly2Tri Contributors
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/// Changes from the Java version
/// attributification
/// Future possibilities
/// Flattening out the number of indirections
/// Replacing arrays of 3 with fixed-length arrays?
/// Replacing bool[3] with a bit array of some sort?
/// Bundling everything into an AoS mess?
/// Hardcode them all as ABC ?
using System;
using System.Diagnostics;
using System.Collections.Generic;
namespace UnityEngine.ProBuilder.Poly2Tri {
class DelaunayTriangle {
public FixedArray3<TriangulationPoint> Points;
public FixedArray3<DelaunayTriangle > Neighbors;
public FixedBitArray3 EdgeIsConstrained, EdgeIsDelaunay;
public bool IsInterior { get; set; }
public DelaunayTriangle(TriangulationPoint p1, TriangulationPoint p2, TriangulationPoint p3) {
Points[0] = p1;
Points[1] = p2;
Points[2] = p3;
}
public int IndexOf(TriangulationPoint p) {
int i = Points.IndexOf(p);
if (i==-1) throw new Exception("Calling index with a point that doesn't exist in triangle");
return i;
}
public int IndexCWFrom (TriangulationPoint p) { return (IndexOf(p)+2)%3; }
public int IndexCCWFrom(TriangulationPoint p) { return (IndexOf(p)+1)%3; }
public bool Contains(TriangulationPoint p) { return Points.Contains(p); }
/// <summary>
/// Update neighbor pointers
/// </summary>
/// <param name="p1">Point 1 of the shared edge</param>
/// <param name="p2">Point 2 of the shared edge</param>
/// <param name="t">This triangle's new neighbor</param>
private void MarkNeighbor( TriangulationPoint p1, TriangulationPoint p2, DelaunayTriangle t ) {
int i = EdgeIndex(p1,p2);
if ( i==-1 ) throw new Exception( "Error marking neighbors -- t doesn't contain edge p1-p2!" );
Neighbors[i] = t;
}
/// <summary>
/// Exhaustive search to update neighbor pointers
/// </summary>
public void MarkNeighbor( DelaunayTriangle t ) {
// Points of this triangle also belonging to t
bool a = t.Contains(Points[0]);
bool b = t.Contains(Points[1]);
bool c = t.Contains(Points[2]);
if (b&&c) { Neighbors[0]=t; t.MarkNeighbor(Points[1],Points[2],this); }
else if (a&&c) { Neighbors[1]=t; t.MarkNeighbor(Points[0],Points[2],this); }
else if (a&&b) { Neighbors[2]=t; t.MarkNeighbor(Points[0],Points[1],this); }
else throw new Exception( "Failed to mark neighbor, doesn't share an edge!");
}
/// <param name="t">Opposite triangle</param>
/// <param name="p">The point in t that isn't shared between the triangles</param>
public TriangulationPoint OppositePoint(DelaunayTriangle t, TriangulationPoint p) {
System.Diagnostics.Debug.Assert(t != this, "self-pointer error");
return PointCWFrom(t.PointCWFrom(p));
}
public DelaunayTriangle NeighborCWFrom (TriangulationPoint point) { return Neighbors[(Points.IndexOf(point)+1)%3]; }
public DelaunayTriangle NeighborCCWFrom (TriangulationPoint point) { return Neighbors[(Points.IndexOf(point)+2)%3]; }
public DelaunayTriangle NeighborAcrossFrom(TriangulationPoint point) { return Neighbors[ Points.IndexOf(point) ]; }
public TriangulationPoint PointCCWFrom(TriangulationPoint point) { return Points[(IndexOf(point)+1)%3]; }
public TriangulationPoint PointCWFrom (TriangulationPoint point) { return Points[(IndexOf(point)+2)%3]; }
private void RotateCW() {
var t = Points[2];
Points[2] = Points[1];
Points[1] = Points[0];
Points[0] = t;
}
/// <summary>
/// Legalize triangle by rotating clockwise around oPoint
/// </summary>
/// <param name="oPoint">The origin point to rotate around</param>
/// <param name="nPoint">???</param>
public void Legalize(TriangulationPoint oPoint, TriangulationPoint nPoint) {
RotateCW();
Points[IndexCCWFrom(oPoint)] = nPoint;
}
public override string ToString() { return Points[0] + "," + Points[1] + "," + Points[2]; }
/// <summary>
/// Finalize edge marking
/// </summary>
public void MarkNeighborEdges() {
for (int i = 0; i < 3; i++) if ( EdgeIsConstrained[i] && Neighbors[i] != null ) {
Neighbors[i].MarkConstrainedEdge(Points[(i+1)%3], Points[(i+2)%3]);
}
}
public void MarkEdge(DelaunayTriangle triangle) {
for (int i = 0; i < 3; i++) if ( EdgeIsConstrained[i] ) {
triangle.MarkConstrainedEdge(Points[(i+1)%3], Points[(i+2)%3]);
}
}
public void MarkEdge(List<DelaunayTriangle> tList) {
foreach ( DelaunayTriangle t in tList )
for ( int i = 0; i < 3; i++ )
if ( t.EdgeIsConstrained[i] )
{
MarkConstrainedEdge( t.Points[(i+1)%3], t.Points[(i+2)%3] );
}
}
public void MarkConstrainedEdge(int index) {
EdgeIsConstrained[index] = true;
}
public void MarkConstrainedEdge(DTSweepConstraint edge) {
MarkConstrainedEdge(edge.P, edge.Q);
}
/// <summary>
/// Mark edge as constrained
/// </summary>
public void MarkConstrainedEdge(TriangulationPoint p, TriangulationPoint q) {
int i = EdgeIndex(p,q);
if ( i != -1 ) EdgeIsConstrained[i] = true;
}
public double Area() {
double b = Points[0].X - Points[1].X;
double h = Points[2].Y - Points[1].Y;
return Math.Abs((b * h * 0.5f));
}
public TriangulationPoint Centroid() {
double cx = (Points[0].X + Points[1].X + Points[2].X) / 3f;
double cy = (Points[0].Y + Points[1].Y + Points[2].Y) / 3f;
return new TriangulationPoint(cx, cy);
}
/// <summary>
/// Get the index of the neighbor that shares this edge (or -1 if it isn't shared)
/// </summary>
/// <returns>index of the shared edge or -1 if edge isn't shared</returns>
public int EdgeIndex(TriangulationPoint p1, TriangulationPoint p2) {
int i1 = Points.IndexOf(p1);
int i2 = Points.IndexOf(p2);
// Points of this triangle in the edge p1-p2
bool a = (i1==0 || i2==0);
bool b = (i1==1 || i2==1);
bool c = (i1==2 || i2==2);
if (b&&c) return 0;
if (a&&c) return 1;
if (a&&b) return 2;
return -1;
}
public bool GetConstrainedEdgeCCW ( TriangulationPoint p ) { return EdgeIsConstrained[(IndexOf(p)+2)%3]; }
public bool GetConstrainedEdgeCW ( TriangulationPoint p ) { return EdgeIsConstrained[(IndexOf(p)+1)%3]; }
public bool GetConstrainedEdgeAcross( TriangulationPoint p ) { return EdgeIsConstrained[ IndexOf(p) ]; }
public void SetConstrainedEdgeCCW ( TriangulationPoint p, bool ce ) { EdgeIsConstrained[(IndexOf(p)+2)%3] = ce; }
public void SetConstrainedEdgeCW ( TriangulationPoint p, bool ce ) { EdgeIsConstrained[(IndexOf(p)+1)%3] = ce; }
public void SetConstrainedEdgeAcross( TriangulationPoint p, bool ce ) { EdgeIsConstrained[ IndexOf(p) ] = ce; }
public bool GetDelaunayEdgeCCW ( TriangulationPoint p ) { return EdgeIsDelaunay[(IndexOf(p)+2)%3]; }
public bool GetDelaunayEdgeCW ( TriangulationPoint p ) { return EdgeIsDelaunay[(IndexOf(p)+1)%3]; }
public bool GetDelaunayEdgeAcross( TriangulationPoint p ) { return EdgeIsDelaunay[ IndexOf(p) ]; }
public void SetDelaunayEdgeCCW ( TriangulationPoint p, bool ce ) { EdgeIsDelaunay[(IndexOf(p)+2)%3] = ce; }
public void SetDelaunayEdgeCW ( TriangulationPoint p, bool ce ) { EdgeIsDelaunay[(IndexOf(p)+1)%3] = ce; }
public void SetDelaunayEdgeAcross( TriangulationPoint p, bool ce ) { EdgeIsDelaunay[ IndexOf(p) ] = ce; }
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ec676d2a8ccecde4f8eb0d32aeaafb98
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9db8260d0bbb07e4bb0356d0b21fbc20
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,133 @@
/* Poly2Tri
* Copyright (c) 2009-2010, Poly2Tri Contributors
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/// Changes from the Java version
/// Removed BST code, but not all artifacts of it
/// Future possibilities
/// Eliminate Add/RemoveNode ?
/// Comments comments and more comments!
using System.Text;
using System;
namespace UnityEngine.ProBuilder.Poly2Tri {
/**
* @author Thomas Åhlen ([email protected])
*/
class AdvancingFront {
public AdvancingFrontNode Head;
public AdvancingFrontNode Tail;
protected AdvancingFrontNode Search;
public AdvancingFront( AdvancingFrontNode head, AdvancingFrontNode tail ) {
this.Head = head;
this.Tail = tail;
this.Search = head;
AddNode(head);
AddNode(tail);
}
public void AddNode( AdvancingFrontNode node ) { }
public void RemoveNode( AdvancingFrontNode node ) { }
public override string ToString() {
StringBuilder sb = new StringBuilder();
AdvancingFrontNode node = Head;
while (node != Tail) {
sb.Append(node.Point.X).Append("->");
node = node.Next;
}
sb.Append(Tail.Point.X);
return sb.ToString();
}
/// <summary>
/// MM: This seems to be used by LocateNode to guess a position in the implicit linked list of AdvancingFrontNodes near x
/// Removed an overload that depended on this being exact
/// </summary>
private AdvancingFrontNode FindSearchNode( double x ) {
return Search;
}
/// <summary>
/// We use a balancing tree to locate a node smaller or equal to given key value (in theory)
/// </summary>
public AdvancingFrontNode LocateNode( TriangulationPoint point ) {
return LocateNode(point.X);
}
private AdvancingFrontNode LocateNode( double x ) {
AdvancingFrontNode node = FindSearchNode(x);
if (x < node.Value) {
while ((node = node.Prev) != null)
if (x >= node.Value) {
Search = node;
return node;
}
} else {
while ((node = node.Next) != null)
if (x < node.Value) {
Search = node.Prev;
return node.Prev;
}
}
return null;
}
/// <summary>
/// This implementation will use simple node traversal algorithm to find a point on the front
/// </summary>
public AdvancingFrontNode LocatePoint( TriangulationPoint point ) {
double px = point.X;
AdvancingFrontNode node = FindSearchNode(px);
double nx = node.Point.X;
if (px == nx) {
if (point != node.Point) {
// We might have two nodes with same x value for a short time
if (point == node.Prev.Point) {
node = node.Prev;
} else if (point == node.Next.Point) {
node = node.Next;
} else {
throw new Exception("Failed to find Node for given afront point");
}
}
} else if (px < nx) {
while ((node = node.Prev) != null) if (point == node.Point) break;
} else {
while ((node = node.Next) != null) if (point == node.Point) break;
}
Search = node;
return node;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 53aa5497892312842a4c059529cd2397
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,54 @@
/* Poly2Tri
* Copyright (c) 2009-2010, Poly2Tri Contributors
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/// Changes from the Java version
/// Removed getters
/// Has* turned into attributes
/// Future possibilities
/// Comments!
namespace UnityEngine.ProBuilder.Poly2Tri {
class AdvancingFrontNode {
public AdvancingFrontNode Next;
public AdvancingFrontNode Prev;
public double Value;
public TriangulationPoint Point;
public DelaunayTriangle Triangle;
public AdvancingFrontNode(TriangulationPoint point) {
this.Point = point;
Value = point.X;
}
public bool HasNext { get { return Next != null; } }
public bool HasPrev { get { return Prev != null; } }
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9d6fbeb339bff0f4cb3744bd60aec40f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,908 @@
/* Poly2Tri
* Copyright (c) 2009-2010, Poly2Tri Contributors
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Sweep-line, Constrained Delauney Triangulation (CDT) See: Domiter, V. and
* Zalik, B.(2008)'Sweep-line algorithm for constrained Delaunay triangulation',
* International Journal of Geographical Information Science
*
* "FlipScan" Constrained Edge Algorithm invented by author of this code.
*
* Author: Thomas Åhlén, [email protected]
*/
/// Changes from the Java version
/// Turned DTSweep into a static class
/// Lots of deindentation via early bailout
/// Future possibilities
/// Comments!
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace UnityEngine.ProBuilder.Poly2Tri {
static class DTSweep {
private const double PI_div2 = Math.PI / 2;
private const double PI_3div4 = 3 * Math.PI / 4;
/// <summary>
/// Triangulate simple polygon with holes
/// </summary>
public static void Triangulate( DTSweepContext tcx ) {
tcx.CreateAdvancingFront();
Sweep(tcx);
// TODO: remove temporary
// Check if the sweep algorithm is legalize robust
// By doing a legalize on all triangles and see if anything happens
// we know if the sweep algorithm missed some legalizations
// Console.WriteLine("============================");
// foreach ( DelaunayTriangle t in tcx.Triangles )
// {
// if( Legalize( tcx, t ) )
// {
// tcx.getDebugContext().setPrimaryTriangle( t );
// Console.WriteLine("[FIX] Triangle needed legalization after sweep");
// }
// }
// Finalize triangulation
if (tcx.TriangulationMode == TriangulationMode.Polygon) {
FinalizationPolygon(tcx);
} else {
FinalizationConvexHull(tcx);
}
tcx.Done();
}
/// <summary>
/// Start sweeping the Y-sorted point set from bottom to top
/// </summary>
private static void Sweep( DTSweepContext tcx ) {
var points = tcx.Points;
TriangulationPoint point;
AdvancingFrontNode node;
for (int i = 1; i < points.Count; i++) {
point = points[i];
node = PointEvent(tcx, point);
if (point.HasEdges) foreach (DTSweepConstraint e in point.Edges) {
if (tcx.IsDebugEnabled) tcx.DTDebugContext.ActiveConstraint = e;
EdgeEvent(tcx, e, node);
}
tcx.Update(null);
}
}
/// <summary>
/// If this is a Delaunay Triangulation of a pointset we need to fill so the triangle mesh gets a ConvexHull
/// </summary>
private static void FinalizationConvexHull( DTSweepContext tcx ) {
AdvancingFrontNode n1, n2;
DelaunayTriangle t1;
TriangulationPoint first, p1;
n1 = tcx.Front.Head.Next;
n2 = n1.Next;
first = n1.Point;
TurnAdvancingFrontConvex(tcx, n1, n2);
n1 = tcx.Front.Tail.Prev;
if (n1.Triangle.Contains(n1.Next.Point) && n1.Triangle.Contains(n1.Prev.Point)) {
t1 = n1.Triangle.NeighborAcrossFrom(n1.Point);
RotateTrianglePair(n1.Triangle, n1.Point, t1, t1.OppositePoint(n1.Triangle, n1.Point));
tcx.MapTriangleToNodes(n1.Triangle);
tcx.MapTriangleToNodes(t1);
}
n1 = tcx.Front.Head.Next;
if (n1.Triangle.Contains(n1.Prev.Point) && n1.Triangle.Contains(n1.Next.Point)) {
t1 = n1.Triangle.NeighborAcrossFrom(n1.Point);
RotateTrianglePair(n1.Triangle, n1.Point, t1, t1.OppositePoint(n1.Triangle, n1.Point));
tcx.MapTriangleToNodes(n1.Triangle);
tcx.MapTriangleToNodes(t1);
}
// TODO: implement ConvexHull for lower right and left boundary
// Lower right boundary
first = tcx.Front.Head.Point;
n2 = tcx.Front.Tail.Prev;
t1 = n2.Triangle;
p1 = n2.Point;
do {
tcx.RemoveFromList(t1);
p1 = t1.PointCCWFrom(p1);
if (p1 == first) break;
t1 = t1.NeighborCCWFrom(p1);
} while (true);
// Lower left boundary
first = tcx.Front.Head.Next.Point;
p1 = t1.PointCWFrom(tcx.Front.Head.Point);
t1 = t1.NeighborCWFrom(tcx.Front.Head.Point);
do {
tcx.RemoveFromList(t1);
p1 = t1.PointCCWFrom(p1);
t1 = t1.NeighborCCWFrom(p1);
} while (p1 != first);
tcx.FinalizeTriangulation();
}
/// <summary>
/// We will traverse the entire advancing front and fill it to form a convex hull.
/// </summary>
private static void TurnAdvancingFrontConvex( DTSweepContext tcx, AdvancingFrontNode b, AdvancingFrontNode c ) {
AdvancingFrontNode first = b;
while (c != tcx.Front.Tail) {
if (tcx.IsDebugEnabled) tcx.DTDebugContext.ActiveNode = c;
if (TriangulationUtil.Orient2d(b.Point, c.Point, c.Next.Point) == Orientation.CCW) {
// [b,c,d] Concave - fill around c
Fill(tcx, c);
c = c.Next;
} else {
// [b,c,d] Convex
if (b != first && TriangulationUtil.Orient2d(b.Prev.Point, b.Point, c.Point) == Orientation.CCW) {
// [a,b,c] Concave - fill around b
Fill(tcx, b);
b = b.Prev;
} else {
// [a,b,c] Convex - nothing to fill
b = c;
c = c.Next;
}
}
}
}
private static void FinalizationPolygon( DTSweepContext tcx ) {
// Get an Internal triangle to start with
DelaunayTriangle t = tcx.Front.Head.Next.Triangle;
TriangulationPoint p = tcx.Front.Head.Next.Point;
while (!t.GetConstrainedEdgeCW(p)) t = t.NeighborCCWFrom(p);
// Collect interior triangles constrained by edges
tcx.MeshClean(t);
}
/// <summary>
/// Find closes node to the left of the new point and
/// create a new triangle. If needed new holes and basins
/// will be filled to.
/// </summary>
private static AdvancingFrontNode PointEvent( DTSweepContext tcx, TriangulationPoint point ) {
AdvancingFrontNode node, newNode;
node = tcx.LocateNode(point);
if (tcx.IsDebugEnabled) tcx.DTDebugContext.ActiveNode = node;
newNode = NewFrontTriangle(tcx, point, node);
// Only need to check +epsilon since point never have smaller
// x value than node due to how we fetch nodes from the front
if (point.X <= node.Point.X + TriangulationUtil.EPSILON) Fill(tcx, node);
tcx.AddNode(newNode);
FillAdvancingFront(tcx, newNode);
return newNode;
}
/// <summary>
/// Creates a new front triangle and legalize it
/// </summary>
private static AdvancingFrontNode NewFrontTriangle( DTSweepContext tcx, TriangulationPoint point, AdvancingFrontNode node ) {
AdvancingFrontNode newNode;
DelaunayTriangle triangle;
triangle = new DelaunayTriangle(point, node.Point, node.Next.Point);
triangle.MarkNeighbor(node.Triangle);
tcx.Triangles.Add(triangle);
newNode = new AdvancingFrontNode(point);
newNode.Next = node.Next;
newNode.Prev = node;
node.Next.Prev = newNode;
node.Next = newNode;
tcx.AddNode(newNode); // XXX: BST
if (tcx.IsDebugEnabled) tcx.DTDebugContext.ActiveNode = newNode;
if (!Legalize(tcx, triangle)) tcx.MapTriangleToNodes(triangle);
return newNode;
}
private static void EdgeEvent( DTSweepContext tcx, DTSweepConstraint edge, AdvancingFrontNode node ) {
try
{
tcx.EdgeEvent.ConstrainedEdge = edge;
tcx.EdgeEvent.Right = edge.P.X > edge.Q.X;
if (tcx.IsDebugEnabled) { tcx.DTDebugContext.PrimaryTriangle = node.Triangle; }
if (IsEdgeSideOfTriangle(node.Triangle, edge.P, edge.Q))
return;
// For now we will do all needed filling
// TODO: integrate with flip process might give some better performance
// but for now this avoid the issue with cases that needs both flips and fills
FillEdgeEvent(tcx, edge, node);
EdgeEvent(tcx, edge.P, edge.Q, node.Triangle, edge.Q);
}
catch { }
// catch (PointOnEdgeException e)
// {
// //Debug.WriteLine( String.Format( "Warning: Skipping Edge: {0}", e.Message ) );
// throw;
// }
}
private static void FillEdgeEvent( DTSweepContext tcx, DTSweepConstraint edge, AdvancingFrontNode node ) {
if (tcx.EdgeEvent.Right) {
FillRightAboveEdgeEvent(tcx, edge, node);
} else {
FillLeftAboveEdgeEvent(tcx, edge, node);
}
}
private static void FillRightConcaveEdgeEvent( DTSweepContext tcx, DTSweepConstraint edge, AdvancingFrontNode node ) {
Fill(tcx, node.Next);
if (node.Next.Point != edge.P) {
// Next above or below edge?
if (TriangulationUtil.Orient2d(edge.Q, node.Next.Point, edge.P) == Orientation.CCW) {
// Below
if (TriangulationUtil.Orient2d(node.Point, node.Next.Point, node.Next.Next.Point) == Orientation.CCW) {
// Next is concave
FillRightConcaveEdgeEvent(tcx, edge, node);
} else {
// Next is convex
}
}
}
}
private static void FillRightConvexEdgeEvent( DTSweepContext tcx, DTSweepConstraint edge, AdvancingFrontNode node ) {
// Next concave or convex?
if (TriangulationUtil.Orient2d(node.Next.Point, node.Next.Next.Point, node.Next.Next.Next.Point) == Orientation.CCW) {
// Concave
FillRightConcaveEdgeEvent(tcx, edge, node.Next);
} else {
// Convex
// Next above or below edge?
if (TriangulationUtil.Orient2d(edge.Q, node.Next.Next.Point, edge.P) == Orientation.CCW) {
// Below
FillRightConvexEdgeEvent(tcx, edge, node.Next);
} else {
// Above
}
}
}
private static void FillRightBelowEdgeEvent( DTSweepContext tcx, DTSweepConstraint edge, AdvancingFrontNode node ) {
if (tcx.IsDebugEnabled) tcx.DTDebugContext.ActiveNode = node;
if (node.Point.X < edge.P.X) { // needed?
if (TriangulationUtil.Orient2d(node.Point, node.Next.Point, node.Next.Next.Point) == Orientation.CCW) {
// Concave
FillRightConcaveEdgeEvent(tcx, edge, node);
} else {
// Convex
FillRightConvexEdgeEvent(tcx, edge, node);
// Retry this one
FillRightBelowEdgeEvent(tcx, edge, node);
}
}
}
private static void FillRightAboveEdgeEvent( DTSweepContext tcx, DTSweepConstraint edge, AdvancingFrontNode node ) {
while (node.Next.Point.X < edge.P.X) {
if (tcx.IsDebugEnabled) { tcx.DTDebugContext.ActiveNode = node; }
// Check if next node is below the edge
Orientation o1 = TriangulationUtil.Orient2d(edge.Q, node.Next.Point, edge.P);
if (o1 == Orientation.CCW) {
FillRightBelowEdgeEvent(tcx, edge, node);
} else {
node = node.Next;
}
}
}
private static void FillLeftConvexEdgeEvent( DTSweepContext tcx, DTSweepConstraint edge, AdvancingFrontNode node ) {
// Next concave or convex?
if (TriangulationUtil.Orient2d(node.Prev.Point, node.Prev.Prev.Point, node.Prev.Prev.Prev.Point) == Orientation.CW) {
// Concave
FillLeftConcaveEdgeEvent(tcx, edge, node.Prev);
} else {
// Convex
// Next above or below edge?
if (TriangulationUtil.Orient2d(edge.Q, node.Prev.Prev.Point, edge.P) == Orientation.CW) {
// Below
FillLeftConvexEdgeEvent(tcx, edge, node.Prev);
} else {
// Above
}
}
}
private static void FillLeftConcaveEdgeEvent( DTSweepContext tcx, DTSweepConstraint edge, AdvancingFrontNode node ) {
Fill(tcx, node.Prev);
if (node.Prev.Point != edge.P) {
// Next above or below edge?
if (TriangulationUtil.Orient2d(edge.Q, node.Prev.Point, edge.P) == Orientation.CW) {
// Below
if (TriangulationUtil.Orient2d(node.Point, node.Prev.Point, node.Prev.Prev.Point) == Orientation.CW) {
// Next is concave
FillLeftConcaveEdgeEvent(tcx, edge, node);
} else {
// Next is convex
}
}
}
}
private static void FillLeftBelowEdgeEvent( DTSweepContext tcx, DTSweepConstraint edge, AdvancingFrontNode node ) {
if (tcx.IsDebugEnabled) tcx.DTDebugContext.ActiveNode = node;
if (node.Point.X > edge.P.X) {
if (TriangulationUtil.Orient2d(node.Point, node.Prev.Point, node.Prev.Prev.Point) == Orientation.CW) {
// Concave
FillLeftConcaveEdgeEvent(tcx, edge, node);
} else {
// Convex
FillLeftConvexEdgeEvent(tcx, edge, node);
// Retry this one
FillLeftBelowEdgeEvent(tcx, edge, node);
}
}
}
private static void FillLeftAboveEdgeEvent( DTSweepContext tcx, DTSweepConstraint edge, AdvancingFrontNode node ) {
while (node.Prev.Point.X > edge.P.X) {
if (tcx.IsDebugEnabled) tcx.DTDebugContext.ActiveNode = node;
// Check if next node is below the edge
Orientation o1 = TriangulationUtil.Orient2d(edge.Q, node.Prev.Point, edge.P);
if (o1 == Orientation.CW) {
FillLeftBelowEdgeEvent(tcx, edge, node);
} else {
node = node.Prev;
}
}
}
private static bool IsEdgeSideOfTriangle( DelaunayTriangle triangle, TriangulationPoint ep, TriangulationPoint eq ) {
int index = triangle.EdgeIndex(ep, eq);
if ( index == -1 ) return false;
triangle.MarkConstrainedEdge(index);
triangle = triangle.Neighbors[index];
if (triangle != null) triangle.MarkConstrainedEdge(ep, eq);
return true;
}
private static void EdgeEvent( DTSweepContext tcx, TriangulationPoint ep, TriangulationPoint eq, DelaunayTriangle triangle, TriangulationPoint point ) {
TriangulationPoint p1, p2;
if (tcx.IsDebugEnabled) tcx.DTDebugContext.PrimaryTriangle=triangle;
if (IsEdgeSideOfTriangle(triangle, ep, eq)) return;
p1 = triangle.PointCCWFrom(point);
Orientation o1 = TriangulationUtil.Orient2d(eq, p1, ep);
if (o1 == Orientation.Collinear) {
// TODO: Split edge in two
//// splitEdge( ep, eq, p1 );
// edgeEvent( tcx, p1, eq, triangle, point );
// edgeEvent( tcx, ep, p1, triangle, p1 );
// return;
throw new PointOnEdgeException("EdgeEvent - Point on constrained edge not supported yet",eq,p1,ep);
}
p2 = triangle.PointCWFrom(point);
Orientation o2 = TriangulationUtil.Orient2d(eq, p2, ep);
if (o2 == Orientation.Collinear) {
// TODO: Split edge in two
// edgeEvent( tcx, p2, eq, triangle, point );
// edgeEvent( tcx, ep, p2, triangle, p2 );
// return;
throw new PointOnEdgeException("EdgeEvent - Point on constrained edge not supported yet",eq,p2,ep);
}
if (o1 == o2) {
// Need to decide if we are rotating CW or CCW to get to a triangle
// that will cross edge
if (o1 == Orientation.CW) {
triangle = triangle.NeighborCCWFrom(point);
} else {
triangle = triangle.NeighborCWFrom(point);
}
EdgeEvent(tcx, ep, eq, triangle, point);
} else {
// This triangle crosses constraint so lets flippin start!
FlipEdgeEvent(tcx, ep, eq, triangle, point);
}
}
/// <summary>
/// In the case of a pointset with some constraint edges. If a triangle side is collinear
/// with a part of the constraint we split the constraint into two constraints. This could
/// happen when the given constraint migth intersect a point in the set.<br>
/// This can never happen in the case when we are working with a polygon.
///
/// Think of two triangles that have non shared sides that are collinear and the constraint
/// is set from a point in triangle A to a point in triangle B so that the constraint is
/// the union of both those sides. We then have to split the constraint into two so we get
/// one constraint for each triangle.
/// </summary>
/// <param name="ep"></param>
/// <param name="eq"></param>
/// <param name="p">point on the edge between ep->eq</param>
private static void SplitEdge( TriangulationPoint ep, TriangulationPoint eq, TriangulationPoint p ) {
DTSweepConstraint edge = eq.Edges.First( e => e.Q==ep || e.P==ep );
edge.P = p;
new DTSweepConstraint(ep, p); // Et tu, Brute? --MM
// // Redo this edge now that we have split the constraint
// newEdgeEvent( tcx, edge, triangle, point );
// // Continue with new edge
// newEdgeEvent( tcx, edge, triangle, p2 );
}
private static void FlipEdgeEvent( DTSweepContext tcx, TriangulationPoint ep, TriangulationPoint eq, DelaunayTriangle t, TriangulationPoint p ) {
DelaunayTriangle ot = t.NeighborAcrossFrom(p);
TriangulationPoint op = ot.OppositePoint(t, p);
if (ot == null) {
// If we want to integrate the fillEdgeEvent do it here
// With current implementation we should never get here
throw new InvalidOperationException("[BUG:FIXME] FLIP failed due to missing triangle");
}
if (tcx.IsDebugEnabled) {
tcx.DTDebugContext.PrimaryTriangle = t;
tcx.DTDebugContext.SecondaryTriangle = ot;
} // TODO: remove
bool inScanArea = TriangulationUtil.InScanArea(p, t.PointCCWFrom(p), t.PointCWFrom(p), op);
if (inScanArea) {
// Lets rotate shared edge one vertex CW
RotateTrianglePair(t, p, ot, op);
tcx.MapTriangleToNodes(t);
tcx.MapTriangleToNodes(ot);
if (p == eq && op == ep) {
if (eq == tcx.EdgeEvent.ConstrainedEdge.Q
&& ep == tcx.EdgeEvent.ConstrainedEdge.P) {
if (tcx.IsDebugEnabled) Console.WriteLine("[FLIP] - constrained edge done"); // TODO: remove
t.MarkConstrainedEdge(ep, eq);
ot.MarkConstrainedEdge(ep, eq);
Legalize(tcx, t);
Legalize(tcx, ot);
} else {
if (tcx.IsDebugEnabled) Console.WriteLine("[FLIP] - subedge done"); // TODO: remove
// XXX: I think one of the triangles should be legalized here?
}
} else {
if (tcx.IsDebugEnabled) Console.WriteLine("[FLIP] - flipping and continuing with triangle still crossing edge"); // TODO: remove
Orientation o = TriangulationUtil.Orient2d(eq, op, ep);
t = NextFlipTriangle(tcx, o, t, ot, p, op);
FlipEdgeEvent(tcx, ep, eq, t, p);
}
} else {
TriangulationPoint newP = NextFlipPoint(ep, eq, ot, op);
FlipScanEdgeEvent(tcx, ep, eq, t, ot, newP);
EdgeEvent(tcx, ep, eq, t, p);
}
}
/// <summary>
/// When we need to traverse from one triangle to the next we need
/// the point in current triangle that is the opposite point to the next
/// triangle.
/// </summary>
private static TriangulationPoint NextFlipPoint( TriangulationPoint ep, TriangulationPoint eq, DelaunayTriangle ot, TriangulationPoint op ) {
Orientation o2d = TriangulationUtil.Orient2d(eq, op, ep);
switch ( o2d ) {
case Orientation.CW: return ot.PointCCWFrom(op);
case Orientation.CCW: return ot.PointCWFrom(op);
case Orientation.Collinear:
// TODO: implement support for point on constraint edge
throw new PointOnEdgeException("Point on constrained edge not supported yet",eq,op,ep);
default:
throw new NotImplementedException("Orientation not handled");
}
}
/// <summary>
/// After a flip we have two triangles and know that only one will still be
/// intersecting the edge. So decide which to contiune with and legalize the other
/// </summary>
/// <param name="tcx"></param>
/// <param name="o">should be the result of an TriangulationUtil.orient2d( eq, op, ep )</param>
/// <param name="t">triangle 1</param>
/// <param name="ot">triangle 2</param>
/// <param name="p">a point shared by both triangles</param>
/// <param name="op">another point shared by both triangles</param>
/// <returns>returns the triangle still intersecting the edge</returns>
private static DelaunayTriangle NextFlipTriangle( DTSweepContext tcx, Orientation o, DelaunayTriangle t, DelaunayTriangle ot, TriangulationPoint p, TriangulationPoint op ) {
int edgeIndex;
if (o == Orientation.CCW) {
// ot is not crossing edge after flip
edgeIndex = ot.EdgeIndex(p, op);
ot.EdgeIsDelaunay[edgeIndex] = true;
Legalize(tcx, ot);
ot.EdgeIsDelaunay.Clear();
return t;
}
// t is not crossing edge after flip
edgeIndex = t.EdgeIndex(p, op);
t.EdgeIsDelaunay[edgeIndex] = true;
Legalize(tcx, t);
t.EdgeIsDelaunay.Clear();
return ot;
}
/// <summary>
/// Scan part of the FlipScan algorithm<br>
/// When a triangle pair isn't flippable we will scan for the next
/// point that is inside the flip triangle scan area. When found
/// we generate a new flipEdgeEvent
/// </summary>
/// <param name="tcx"></param>
/// <param name="ep">last point on the edge we are traversing</param>
/// <param name="eq">first point on the edge we are traversing</param>
/// <param name="flipTriangle">the current triangle sharing the point eq with edge</param>
/// <param name="t"></param>
/// <param name="p"></param>
private static void FlipScanEdgeEvent( DTSweepContext tcx, TriangulationPoint ep, TriangulationPoint eq, DelaunayTriangle flipTriangle, DelaunayTriangle t, TriangulationPoint p ) {
DelaunayTriangle ot;
TriangulationPoint op, newP;
bool inScanArea;
ot = t.NeighborAcrossFrom(p);
op = ot.OppositePoint(t, p);
if (ot == null) {
// If we want to integrate the fillEdgeEvent do it here
// With current implementation we should never get here
throw new Exception("[BUG:FIXME] FLIP failed due to missing triangle");
}
if (tcx.IsDebugEnabled) {
Console.WriteLine("[FLIP:SCAN] - scan next point"); // TODO: remove
tcx.DTDebugContext.PrimaryTriangle = t;
tcx.DTDebugContext.SecondaryTriangle = ot;
}
inScanArea = TriangulationUtil.InScanArea(eq, flipTriangle.PointCCWFrom(eq), flipTriangle.PointCWFrom(eq), op);
if (inScanArea) {
// flip with new edge op->eq
FlipEdgeEvent(tcx, eq, op, ot, op);
// TODO: Actually I just figured out that it should be possible to
// improve this by getting the next ot and op before the the above
// flip and continue the flipScanEdgeEvent here
// set new ot and op here and loop back to inScanArea test
// also need to set a new flipTriangle first
// Turns out at first glance that this is somewhat complicated
// so it will have to wait.
} else {
newP = NextFlipPoint(ep, eq, ot, op);
FlipScanEdgeEvent(tcx, ep, eq, flipTriangle, ot, newP);
}
}
/// <summary>
/// Fills holes in the Advancing Front
/// </summary>
private static void FillAdvancingFront( DTSweepContext tcx, AdvancingFrontNode n ) {
AdvancingFrontNode node;
double angle;
// Fill right holes
node = n.Next;
while (node.HasNext) {
angle = HoleAngle(node);
if (angle > PI_div2 || angle < -PI_div2) break;
Fill(tcx, node);
node = node.Next;
}
// Fill left holes
node = n.Prev;
while (node.HasPrev) {
angle = HoleAngle(node);
if (angle > PI_div2 || angle < -PI_div2) break;
Fill(tcx, node);
node = node.Prev;
}
// Fill right basins
if (n.HasNext && n.Next.HasNext) {
angle = BasinAngle(n);
if (angle < PI_3div4) FillBasin(tcx, n);
}
}
/// <summary>
/// Fills a basin that has formed on the Advancing Front to the right
/// of given node.<br>
/// First we decide a left,bottom and right node that forms the
/// boundaries of the basin. Then we do a reqursive fill.
/// </summary>
/// <param name="tcx"></param>
/// <param name="node">starting node, this or next node will be left node</param>
private static void FillBasin( DTSweepContext tcx, AdvancingFrontNode node ) {
if (TriangulationUtil.Orient2d(node.Point, node.Next.Point, node.Next.Next.Point) == Orientation.CCW) {
// tcx.basin.leftNode = node.next.next;
tcx.Basin.leftNode = node;
} else {
tcx.Basin.leftNode = node.Next;
}
// Find the bottom and right node
tcx.Basin.bottomNode = tcx.Basin.leftNode;
while (tcx.Basin.bottomNode.HasNext && tcx.Basin.bottomNode.Point.Y >= tcx.Basin.bottomNode.Next.Point.Y) tcx.Basin.bottomNode = tcx.Basin.bottomNode.Next;
if (tcx.Basin.bottomNode == tcx.Basin.leftNode) return; // No valid basin
tcx.Basin.rightNode = tcx.Basin.bottomNode;
while (tcx.Basin.rightNode.HasNext && tcx.Basin.rightNode.Point.Y < tcx.Basin.rightNode.Next.Point.Y) tcx.Basin.rightNode = tcx.Basin.rightNode.Next;
if (tcx.Basin.rightNode == tcx.Basin.bottomNode) return; // No valid basins
tcx.Basin.width = tcx.Basin.rightNode.Point.X - tcx.Basin.leftNode.Point.X;
tcx.Basin.leftHighest = tcx.Basin.leftNode.Point.Y > tcx.Basin.rightNode.Point.Y;
FillBasinReq(tcx, tcx.Basin.bottomNode);
}
/// <summary>
/// Recursive algorithm to fill a Basin with triangles
/// </summary>
private static void FillBasinReq( DTSweepContext tcx, AdvancingFrontNode node ) {
if (IsShallow(tcx, node)) return; // if shallow stop filling
Fill(tcx, node);
if (node.Prev == tcx.Basin.leftNode && node.Next == tcx.Basin.rightNode) {
return;
} else if (node.Prev == tcx.Basin.leftNode) {
Orientation o = TriangulationUtil.Orient2d(node.Point, node.Next.Point, node.Next.Next.Point);
if (o == Orientation.CW) return;
node = node.Next;
} else if (node.Next == tcx.Basin.rightNode) {
Orientation o = TriangulationUtil.Orient2d(node.Point, node.Prev.Point, node.Prev.Prev.Point);
if (o == Orientation.CCW) return;
node = node.Prev;
} else {
// Continue with the neighbor node with lowest Y value
if (node.Prev.Point.Y < node.Next.Point.Y) {
node = node.Prev;
} else {
node = node.Next;
}
}
FillBasinReq(tcx, node);
}
private static bool IsShallow( DTSweepContext tcx, AdvancingFrontNode node ) {
double height;
if (tcx.Basin.leftHighest) {
height = tcx.Basin.leftNode.Point.Y - node.Point.Y;
} else {
height = tcx.Basin.rightNode.Point.Y - node.Point.Y;
}
if (tcx.Basin.width > height) {
return true;
}
return false;
}
/// <summary>
/// ???
/// </summary>
/// <param name="node">middle node</param>
/// <returns>the angle between 3 front nodes</returns>
private static double HoleAngle( AdvancingFrontNode node ) {
// XXX: do we really need a signed angle for holeAngle?
// could possible save some cycles here
/* Complex plane
* ab = cosA +i*sinA
* ab = (ax + ay*i)(bx + by*i) = (ax*bx + ay*by) + i(ax*by-ay*bx)
* atan2(y,x) computes the principal value of the argument function
* applied to the complex number x+iy
* Where x = ax*bx + ay*by
* y = ax*by - ay*bx
*/
double px = node.Point.X;
double py = node.Point.Y;
double ax = node.Next.Point.X - px;
double ay = node.Next.Point.Y - py;
double bx = node.Prev.Point.X - px;
double by = node.Prev.Point.Y - py;
return Math.Atan2(ax * by - ay * bx, ax * bx + ay * by);
}
/// <summary>
/// The basin angle is decided against the horizontal line [1,0]
/// </summary>
private static double BasinAngle( AdvancingFrontNode node ) {
double ax = node.Point.X - node.Next.Next.Point.X;
double ay = node.Point.Y - node.Next.Next.Point.Y;
return Math.Atan2(ay, ax);
}
/// <summary>
/// Adds a triangle to the advancing front to fill a hole.
/// </summary>
/// <param name="tcx"></param>
/// <param name="node">middle node, that is the bottom of the hole</param>
private static void Fill( DTSweepContext tcx, AdvancingFrontNode node ) {
DelaunayTriangle triangle = new DelaunayTriangle(node.Prev.Point, node.Point, node.Next.Point);
// TODO: should copy the cEdge value from neighbor triangles
// for now cEdge values are copied during the legalize
triangle.MarkNeighbor(node.Prev.Triangle);
triangle.MarkNeighbor(node.Triangle);
tcx.Triangles.Add(triangle);
// Update the advancing front
node.Prev.Next = node.Next;
node.Next.Prev = node.Prev;
tcx.RemoveNode(node);
// If it was legalized the triangle has already been mapped
if (!Legalize(tcx, triangle)) tcx.MapTriangleToNodes(triangle);
}
/// <summary>
/// Returns true if triangle was legalized
/// </summary>
private static bool Legalize( DTSweepContext tcx, DelaunayTriangle t ) {
// To legalize a triangle we start by finding if any of the three edges
// violate the Delaunay condition
for (int i = 0; i < 3; i++) {
// TODO: fix so that cEdge is always valid when creating new triangles then we can check it here
// instead of below with ot
if (t.EdgeIsDelaunay[i]) continue;
DelaunayTriangle ot = t.Neighbors[i];
if (ot == null) continue;
TriangulationPoint p = t.Points[i];
TriangulationPoint op = ot.OppositePoint(t, p);
int oi = ot.IndexOf(op);
// If this is a Constrained Edge or a Delaunay Edge(only during recursive legalization)
// then we should not try to legalize
if (ot.EdgeIsConstrained[oi] || ot.EdgeIsDelaunay[oi]) {
t.EdgeIsConstrained[i] = ot.EdgeIsConstrained[oi]; // XXX: have no good way of setting this property when creating new triangles so lets set it here
continue;
}
if (!TriangulationUtil.SmartIncircle(p,t.PointCCWFrom(p),t.PointCWFrom(p),op)) continue;
// Lets mark this shared edge as Delaunay
t.EdgeIsDelaunay[i] = true;
ot.EdgeIsDelaunay[oi] = true;
// Lets rotate shared edge one vertex CW to legalize it
RotateTrianglePair(t, p, ot, op);
// We now got one valid Delaunay Edge shared by two triangles
// This gives us 4 new edges to check for Delaunay
// Make sure that triangle to node mapping is done only one time for a specific triangle
if (!Legalize(tcx, t)) tcx.MapTriangleToNodes(t);
if (!Legalize(tcx, ot)) tcx.MapTriangleToNodes(ot);
// Reset the Delaunay edges, since they only are valid Delaunay edges
// until we add a new triangle or point.
// XXX: need to think about this. Can these edges be tried after we
// return to previous recursive level?
t.EdgeIsDelaunay[i] = false;
ot.EdgeIsDelaunay[oi] = false;
// If triangle have been legalized no need to check the other edges since
// the recursive legalization will handles those so we can end here.
return true;
}
return false;
}
/// <summary>
/// Rotates a triangle pair one vertex CW
/// n2 n2
/// P +-----+ P +-----+
/// | t /| |\ t |
/// | / | | \ |
/// n1| / |n3 n1| \ |n3
/// | / | after CW | \ |
/// |/ oT | | oT \|
/// +-----+ oP +-----+
/// n4 n4
/// </summary>
private static void RotateTrianglePair( DelaunayTriangle t, TriangulationPoint p, DelaunayTriangle ot, TriangulationPoint op ) {
DelaunayTriangle n1, n2, n3, n4;
n1 = t.NeighborCCWFrom(p);
n2 = t.NeighborCWFrom(p);
n3 = ot.NeighborCCWFrom(op);
n4 = ot.NeighborCWFrom(op);
bool ce1, ce2, ce3, ce4;
ce1 = t.GetConstrainedEdgeCCW(p);
ce2 = t.GetConstrainedEdgeCW(p);
ce3 = ot.GetConstrainedEdgeCCW(op);
ce4 = ot.GetConstrainedEdgeCW(op);
bool de1, de2, de3, de4;
de1 = t.GetDelaunayEdgeCCW(p);
de2 = t.GetDelaunayEdgeCW(p);
de3 = ot.GetDelaunayEdgeCCW(op);
de4 = ot.GetDelaunayEdgeCW(op);
t.Legalize(p, op);
ot.Legalize(op, p);
// Remap dEdge
ot.SetDelaunayEdgeCCW(p, de1);
t.SetDelaunayEdgeCW(p, de2);
t.SetDelaunayEdgeCCW(op, de3);
ot.SetDelaunayEdgeCW(op, de4);
// Remap cEdge
ot.SetConstrainedEdgeCCW(p, ce1);
t.SetConstrainedEdgeCW(p, ce2);
t.SetConstrainedEdgeCCW(op, ce3);
ot.SetConstrainedEdgeCW(op, ce4);
// Remap neighbors
// XXX: might optimize the markNeighbor by keeping track of
// what side should be assigned to what neighbor after the
// rotation. Now mark neighbor does lots of testing to find
// the right side.
t.Neighbors.Clear();
ot.Neighbors.Clear();
if (n1 != null) ot.MarkNeighbor(n1);
if (n2 != null) t.MarkNeighbor(n2);
if (n3 != null) t.MarkNeighbor(n3);
if (n4 != null) ot.MarkNeighbor(n4);
t.MarkNeighbor(ot);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2b3f77c5dfa698a478ad5d291674b8a5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,40 @@
/* Poly2Tri
* Copyright (c) 2009-2010, Poly2Tri Contributors
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace UnityEngine.ProBuilder.Poly2Tri {
class DTSweepBasin {
public AdvancingFrontNode leftNode;
public AdvancingFrontNode bottomNode;
public AdvancingFrontNode rightNode;
public double width;
public bool leftHighest;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d6658a80b0b3b964281a5312b39c9203
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,57 @@
/* Poly2Tri
* Copyright (c) 2009-2010, Poly2Tri Contributors
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace UnityEngine.ProBuilder.Poly2Tri {
class DTSweepConstraint : TriangulationConstraint {
/// <summary>
/// Give two points in any order. Will always be ordered so
/// that q.y > p.y and q.x > p.x if same y value
/// </summary>
public DTSweepConstraint( TriangulationPoint p1, TriangulationPoint p2 ) {
P = p1;
Q = p2;
if (p1.Y > p2.Y) {
Q = p1;
P = p2;
} else if (p1.Y == p2.Y) {
if (p1.X > p2.X) {
Q = p1;
P = p2;
} else if (p1.X == p2.X) {
// logger.info( "Failed to create constraint {}={}", p1, p2 );
// throw new DuplicatePointException( p1 + "=" + p2 );
// return;
}
}
Q.AddEdge(this);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2efa3d03c26294747aaa61c2a09ac41f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,197 @@
/* Poly2Tri
* Copyright (c) 2009-2010, Poly2Tri Contributors
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace UnityEngine.ProBuilder.Poly2Tri {
/**
*
* @author Thomas Åhlén, [email protected]
*
*/
class DTSweepContext : TriangulationContext {
// Inital triangle factor, seed triangle will extend 30% of
// PointSet width to both left and right.
private readonly float ALPHA = 0.3f;
public AdvancingFront Front;
public TriangulationPoint Head { get; set; }
public TriangulationPoint Tail { get; set; }
public DTSweepBasin Basin = new DTSweepBasin();
public DTSweepEdgeEvent EdgeEvent = new DTSweepEdgeEvent();
private DTSweepPointComparator _comparator = new DTSweepPointComparator();
public DTSweepContext() {
Clear();
}
public override bool IsDebugEnabled { get {
return base.IsDebugEnabled;
} protected set {
if (value && DebugContext == null) DebugContext = new DTSweepDebugContext(this);
base.IsDebugEnabled = value;
}}
public void RemoveFromList( DelaunayTriangle triangle ) {
Triangles.Remove(triangle);
// TODO: remove all neighbor pointers to this triangle
// for( int i=0; i<3; i++ )
// {
// if( triangle.neighbors[i] != null )
// {
// triangle.neighbors[i].clearNeighbor( triangle );
// }
// }
// triangle.clearNeighbors();
}
public void MeshClean( DelaunayTriangle triangle ) {
MeshCleanReq(triangle);
}
private void MeshCleanReq( DelaunayTriangle triangle ) {
if (triangle != null && !triangle.IsInterior) {
triangle.IsInterior = true;
Triangulatable.AddTriangle(triangle);
for (int i = 0; i < 3; i++)
if (!triangle.EdgeIsConstrained[i])
{
MeshCleanReq(triangle.Neighbors[i]);
}
}
}
public override void Clear() {
base.Clear();
Triangles.Clear();
}
public void AddNode( AdvancingFrontNode node ) {
// Console.WriteLine( "add:" + node.key + ":" + System.identityHashCode(node.key));
// m_nodeTree.put( node.getKey(), node );
Front.AddNode(node);
}
public void RemoveNode( AdvancingFrontNode node ) {
// Console.WriteLine( "remove:" + node.key + ":" + System.identityHashCode(node.key));
// m_nodeTree.delete( node.getKey() );
Front.RemoveNode(node);
}
public AdvancingFrontNode LocateNode( TriangulationPoint point ) {
return Front.LocateNode(point);
}
public void CreateAdvancingFront() {
AdvancingFrontNode head, tail, middle;
// Initial triangle
DelaunayTriangle iTriangle = new DelaunayTriangle(Points[0], Tail, Head);
Triangles.Add(iTriangle);
head = new AdvancingFrontNode(iTriangle.Points[1]);
head.Triangle = iTriangle;
middle = new AdvancingFrontNode(iTriangle.Points[0]);
middle.Triangle = iTriangle;
tail = new AdvancingFrontNode(iTriangle.Points[2]);
Front = new AdvancingFront(head, tail);
Front.AddNode(middle);
// TODO: I think it would be more intuitive if head is middles next and not previous
// so swap head and tail
Front.Head.Next = middle;
middle.Next = Front.Tail;
middle.Prev = Front.Head;
Front.Tail.Prev = middle;
}
/// <summary>
/// Try to map a node to all sides of this triangle that don't have
/// a neighbor.
/// </summary>
public void MapTriangleToNodes( DelaunayTriangle t ) {
for (int i = 0; i < 3; i++)
if (t.Neighbors[i] == null)
{
AdvancingFrontNode n = Front.LocatePoint(t.PointCWFrom(t.Points[i]));
if (n != null) n.Triangle = t;
}
}
public override void PrepareTriangulation( Triangulatable t ) {
base.PrepareTriangulation(t);
double xmax, xmin;
double ymax, ymin;
xmax = xmin = Points[0].X;
ymax = ymin = Points[0].Y;
// Calculate bounds. Should be combined with the sorting
foreach (TriangulationPoint p in Points) {
if (p.X > xmax) xmax = p.X;
if (p.X < xmin) xmin = p.X;
if (p.Y > ymax) ymax = p.Y;
if (p.Y < ymin) ymin = p.Y;
}
double deltaX = ALPHA * (xmax - xmin);
double deltaY = ALPHA * (ymax - ymin);
TriangulationPoint p1 = new TriangulationPoint(xmax + deltaX, ymin - deltaY, -1);
TriangulationPoint p2 = new TriangulationPoint(xmin - deltaX, ymin - deltaY, -1);
Head = p1;
Tail = p2;
// long time = System.nanoTime();
// Sort the points along y-axis
Points.Sort(_comparator);
// logger.info( "Triangulation setup [{}ms]", ( System.nanoTime() - time ) / 1e6 );
}
public void FinalizeTriangulation() {
Triangulatable.AddTriangles(Triangles);
Triangles.Clear();
}
public override TriangulationConstraint NewConstraint( TriangulationPoint a, TriangulationPoint b ) {
return new DTSweepConstraint(a, b);
}
public override TriangulationAlgorithm Algorithm { get { return TriangulationAlgorithm.DTSweep; }}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a943be722733e6741978a775d47f8e2c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,62 @@
/* Poly2Tri
* Copyright (c) 2009-2010, Poly2Tri Contributors
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace UnityEngine.ProBuilder.Poly2Tri {
class DTSweepDebugContext : TriangulationDebugContext {
/*
* Fields used for visual representation of current triangulation
*/
public DelaunayTriangle PrimaryTriangle { get { return _primaryTriangle ; } set { _primaryTriangle = value; _tcx.Update("set PrimaryTriangle"); } }
public DelaunayTriangle SecondaryTriangle { get { return _secondaryTriangle; } set { _secondaryTriangle = value; _tcx.Update("set SecondaryTriangle"); } }
public TriangulationPoint ActivePoint { get { return _activePoint ; } set { _activePoint = value; _tcx.Update("set ActivePoint"); } }
public AdvancingFrontNode ActiveNode { get { return _activeNode ; } set { _activeNode = value; _tcx.Update("set ActiveNode"); } }
public DTSweepConstraint ActiveConstraint { get { return _activeConstraint ; } set { _activeConstraint = value; _tcx.Update("set ActiveConstraint"); } }
public DTSweepDebugContext( DTSweepContext tcx ) : base(tcx) { }
public bool IsDebugContext { get { return true; } }
public override void Clear() {
PrimaryTriangle = null;
SecondaryTriangle = null;
ActivePoint = null;
ActiveNode = null;
ActiveConstraint = null;
}
private DelaunayTriangle _primaryTriangle;
private DelaunayTriangle _secondaryTriangle;
private TriangulationPoint _activePoint;
private AdvancingFrontNode _activeNode;
private DTSweepConstraint _activeConstraint;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fe5e031b8d5fd9d43b054ae08c843652
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,40 @@
/* Poly2Tri
* Copyright (c) 2009-2010, Poly2Tri Contributors
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/// Changes from the Java version
/// Turned DTSweepEdgeEvent into a value type
namespace UnityEngine.ProBuilder.Poly2Tri {
class DTSweepEdgeEvent {
public DTSweepConstraint ConstrainedEdge;
public bool Right;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9c90a3667d9b2154bb538feb303989f8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,52 @@
/* Poly2Tri
* Copyright (c) 2009-2010, Poly2Tri Contributors
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System.Collections.Generic;
namespace UnityEngine.ProBuilder.Poly2Tri {
class DTSweepPointComparator : IComparer<TriangulationPoint> {
public int Compare(TriangulationPoint p1, TriangulationPoint p2) {
if (p1.Y < p2.Y) {
return -1;
} else if (p1.Y > p2.Y) {
return 1;
} else {
if (p1.X < p2.X) {
return -1;
} else if (p1.X > p2.X) {
return 1;
} else {
return 0;
}
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 881b617b0cb15574f93592b7b6a3e373
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,46 @@
/* Poly2Tri
* Copyright (c) 2009-2010, Poly2Tri Contributors
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
namespace UnityEngine.ProBuilder.Poly2Tri {
class PointOnEdgeException : NotImplementedException {
public readonly TriangulationPoint A,B,C;
public PointOnEdgeException( string message, TriangulationPoint a, TriangulationPoint b, TriangulationPoint c )
: base(message)
{
A=a;
B=b;
C=c;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 659601bba69c8e747bcc79ca9e877a2f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,47 @@
/* Poly2Tri
* Copyright (c) 2009-2010, Poly2Tri Contributors
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System.Collections.Generic;
namespace UnityEngine.ProBuilder.Poly2Tri {
interface Triangulatable {
void Prepare(TriangulationContext tcx);
IList<TriangulationPoint> Points { get; } // MM: Neither of these are used via interface (yet?)
IList<DelaunayTriangle> Triangles { get; }
void AddTriangle(DelaunayTriangle t);
void AddTriangles(IEnumerable<DelaunayTriangle> list);
void ClearTriangles();
TriangulationMode TriangulationMode { get; }
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3d263593a085cdf469778e9ea07ba483
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,36 @@
/* Poly2Tri
* Copyright (c) 2009-2010, Poly2Tri Contributors
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace UnityEngine.ProBuilder.Poly2Tri {
enum Orientation {
CW, CCW, Collinear
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6e4871d32a3aac4448c0c3f9e182f972
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9f85da24fbeceaa4e96e6881fe6fac37
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,61 @@
/* Poly2Tri
* Copyright (c) 2009-2010, Poly2Tri Contributors
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System.Collections.Generic;
namespace UnityEngine.ProBuilder.Poly2Tri {
/*
* Extends the PointSet by adding some Constraints on how it will be triangulated<br>
* A constraint defines an edge between two points in the set, these edges can not
* be crossed. They will be enforced triangle edges after a triangulation.
* <p>
*
*
* @author Thomas Åhlén, [email protected]
*/
class ConstrainedPointSet : PointSet {
public int[] EdgeIndex { get; private set; }
public ConstrainedPointSet(List<TriangulationPoint> points, int[] index)
: base(points) {
EdgeIndex = index;
}
public override TriangulationMode TriangulationMode { get { return TriangulationMode.Constrained; } }
public override void Prepare(TriangulationContext tcx) {
base.Prepare(tcx);
for (int i = 0; i < EdgeIndex.Length; i += 2) {
// XXX: must change!!
tcx.NewConstraint(Points[EdgeIndex[i]], Points[EdgeIndex[i + 1]]);
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 92edb19c798910a47b4dec2660fcd96d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,66 @@
/* Poly2Tri
* Copyright (c) 2009-2010, Poly2Tri Contributors
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System.Collections.Generic;
namespace UnityEngine.ProBuilder.Poly2Tri {
class PointSet : Triangulatable {
public IList<TriangulationPoint> Points { get; private set; }
public IList<DelaunayTriangle> Triangles { get; private set; }
public PointSet(List<TriangulationPoint> points) {
Points = new List<TriangulationPoint>(points);
}
public virtual TriangulationMode TriangulationMode { get { return TriangulationMode.Unconstrained; }}
public void AddTriangle(DelaunayTriangle t) {
Triangles.Add(t);
}
public void AddTriangles(IEnumerable<DelaunayTriangle> list) {
foreach ( var tri in list ) Triangles.Add(tri);
}
public void ClearTriangles() {
Triangles.Clear();
}
public virtual void Prepare(TriangulationContext tcx) {
if (Triangles == null) {
Triangles = new List<DelaunayTriangle>(Points.Count);
} else {
Triangles.Clear();
}
tcx.Points.AddRange(Points);
}
}
}

Some files were not shown because too many files have changed in this diff Show More