Files
2026-03-04 10:03:45 +08:00

686 lines
16 KiB
C#

using System;
using System.Collections.Generic;
using TriangleNet.Geometry;
using TriangleNet.Logging;
using TriangleNet.Meshing.Iterators;
using TriangleNet.Topology;
namespace TriangleNet.Meshing
{
internal class ConstraintMesher
{
private IPredicates predicates;
private Mesh mesh;
private Behavior behavior;
private TriangleLocator locator;
private List<Triangle> viri;
private ILog<LogItem> logger;
public ConstraintMesher(Mesh mesh, Configuration config)
{
this.mesh = mesh;
predicates = config.Predicates();
behavior = mesh.behavior;
locator = mesh.locator;
viri = new List<Triangle>();
logger = Log.Instance;
}
public void Apply(IPolygon input, ConstraintOptions options)
{
behavior.Poly = input.Segments.Count > 0;
if (options != null)
{
behavior.ConformingDelaunay = options.ConformingDelaunay;
behavior.Convex = options.Convex;
behavior.NoBisect = options.SegmentSplitting;
if (behavior.ConformingDelaunay)
{
behavior.Quality = true;
}
}
behavior.useRegions = input.Regions.Count > 0;
mesh.infvertex1 = null;
mesh.infvertex2 = null;
mesh.infvertex3 = null;
if (behavior.useSegments)
{
mesh.checksegments = true;
FormSkeleton(input);
}
if (behavior.Poly && mesh.triangles.Count > 0)
{
mesh.holes.AddRange(input.Holes);
mesh.regions.AddRange(input.Regions);
CarveHoles();
}
}
private void CarveHoles()
{
Otri searchtri = default(Otri);
Triangle[] array = null;
Triangle dummytri = mesh.dummytri;
if (!mesh.behavior.Convex)
{
InfectHull();
}
if (!mesh.behavior.NoHoles)
{
foreach (Point hole in mesh.holes)
{
if (mesh.bounds.Contains(hole))
{
searchtri.tri = dummytri;
searchtri.orient = 0;
searchtri.Sym();
Vertex a = searchtri.Org();
Vertex b = searchtri.Dest();
if (predicates.CounterClockwise(a, b, hole) > 0.0 && mesh.locator.Locate(hole, ref searchtri) != LocateResult.Outside && !searchtri.IsInfected())
{
searchtri.Infect();
viri.Add(searchtri.tri);
}
}
}
}
if (mesh.regions.Count > 0)
{
int num = 0;
array = new Triangle[mesh.regions.Count];
foreach (RegionPointer region in mesh.regions)
{
array[num] = dummytri;
if (mesh.bounds.Contains(region.point))
{
searchtri.tri = dummytri;
searchtri.orient = 0;
searchtri.Sym();
Vertex a = searchtri.Org();
Vertex b = searchtri.Dest();
if (predicates.CounterClockwise(a, b, region.point) > 0.0 && mesh.locator.Locate(region.point, ref searchtri) != LocateResult.Outside && !searchtri.IsInfected())
{
array[num] = searchtri.tri;
array[num].label = region.id;
array[num].area = region.area;
}
}
num++;
}
}
if (viri.Count > 0)
{
Plague();
}
if (array != null)
{
RegionIterator regionIterator = new RegionIterator(mesh);
for (int i = 0; i < array.Length; i++)
{
if (array[i].id != -1 && !Otri.IsDead(array[i]))
{
regionIterator.Process(array[i]);
}
}
}
viri.Clear();
}
private void FormSkeleton(IPolygon input)
{
mesh.insegments = 0;
if (behavior.Poly)
{
if (mesh.triangles.Count == 0)
{
return;
}
if (input.Segments.Count > 0)
{
mesh.MakeVertexMap();
}
foreach (ISegment segment in input.Segments)
{
mesh.insegments++;
Vertex vertex = segment.GetVertex(0);
Vertex vertex2 = segment.GetVertex(1);
if (vertex.x == vertex2.x && vertex.y == vertex2.y)
{
if (Log.Verbose)
{
logger.Warning("Endpoints of segment (IDs " + vertex.id + "/" + vertex2.id + ") are coincident.", "Mesh.FormSkeleton()");
}
}
else
{
InsertSegment(vertex, vertex2, segment.Label);
}
}
}
if (behavior.Convex || !behavior.Poly)
{
MarkHull();
}
}
private void InfectHull()
{
Otri ot = default(Otri);
Otri ot2 = default(Otri);
Otri ot3 = default(Otri);
Osub os = default(Osub);
Triangle dummytri = mesh.dummytri;
ot.tri = dummytri;
ot.orient = 0;
ot.Sym();
ot.Copy(ref ot3);
do
{
if (!ot.IsInfected())
{
ot.Pivot(ref os);
if (os.seg.hash == -1)
{
if (!ot.IsInfected())
{
ot.Infect();
viri.Add(ot.tri);
}
}
else if (os.seg.boundary == 0)
{
os.seg.boundary = 1;
Vertex vertex = ot.Org();
Vertex vertex2 = ot.Dest();
if (vertex.label == 0)
{
vertex.label = 1;
}
if (vertex2.label == 0)
{
vertex2.label = 1;
}
}
}
ot.Lnext();
ot.Oprev(ref ot2);
while (ot2.tri.id != -1)
{
ot2.Copy(ref ot);
ot.Oprev(ref ot2);
}
}
while (!ot.Equals(ot3));
}
private void Plague()
{
Otri ot = default(Otri);
Otri ot2 = default(Otri);
Osub os = default(Osub);
SubSegment dummysub = mesh.dummysub;
Triangle dummytri = mesh.dummytri;
for (int i = 0; i < viri.Count; i++)
{
ot.tri = viri[i];
ot.Uninfect();
ot.orient = 0;
while (ot.orient < 3)
{
ot.Sym(ref ot2);
ot.Pivot(ref os);
if (ot2.tri.id == -1 || ot2.IsInfected())
{
if (os.seg.hash != -1)
{
mesh.SubsegDealloc(os.seg);
if (ot2.tri.id != -1)
{
ot2.Uninfect();
ot2.SegDissolve(dummysub);
ot2.Infect();
}
}
}
else if (os.seg.hash == -1)
{
ot2.Infect();
viri.Add(ot2.tri);
}
else
{
os.TriDissolve(dummytri);
if (os.seg.boundary == 0)
{
os.seg.boundary = 1;
}
Vertex vertex = ot2.Org();
Vertex vertex2 = ot2.Dest();
if (vertex.label == 0)
{
vertex.label = 1;
}
if (vertex2.label == 0)
{
vertex2.label = 1;
}
}
ot.orient++;
}
ot.Infect();
}
foreach (Triangle virus in viri)
{
ot.tri = virus;
ot.orient = 0;
while (ot.orient < 3)
{
Vertex vertex3 = ot.Org();
if (vertex3 != null)
{
bool flag = true;
ot.SetOrg(null);
ot.Onext(ref ot2);
while (ot2.tri.id != -1 && !ot2.Equals(ot))
{
if (ot2.IsInfected())
{
ot2.SetOrg(null);
}
else
{
flag = false;
}
ot2.Onext();
}
if (ot2.tri.id == -1)
{
ot.Oprev(ref ot2);
while (ot2.tri.id != -1)
{
if (ot2.IsInfected())
{
ot2.SetOrg(null);
}
else
{
flag = false;
}
ot2.Oprev();
}
}
if (flag)
{
vertex3.type = VertexType.UndeadVertex;
mesh.undeads++;
}
}
ot.orient++;
}
ot.orient = 0;
while (ot.orient < 3)
{
ot.Sym(ref ot2);
if (ot2.tri.id == -1)
{
mesh.hullsize--;
}
else
{
ot2.Dissolve(dummytri);
mesh.hullsize++;
}
ot.orient++;
}
mesh.TriangleDealloc(ot.tri);
}
viri.Clear();
}
private FindDirectionResult FindDirection(ref Otri searchtri, Vertex searchpoint)
{
Otri ot = default(Otri);
Vertex vertex = searchtri.Org();
Vertex c = searchtri.Dest();
Vertex c2 = searchtri.Apex();
double num = predicates.CounterClockwise(searchpoint, vertex, c2);
bool flag = num > 0.0;
double num2 = predicates.CounterClockwise(vertex, searchpoint, c);
bool flag2 = num2 > 0.0;
if (flag && flag2)
{
searchtri.Onext(ref ot);
if (ot.tri.id == -1)
{
flag = false;
}
else
{
flag2 = false;
}
}
while (flag)
{
searchtri.Onext();
if (searchtri.tri.id == -1)
{
logger.Error("Unable to find a triangle on path.", "Mesh.FindDirection().1");
throw new Exception("Unable to find a triangle on path.");
}
c2 = searchtri.Apex();
num2 = num;
num = predicates.CounterClockwise(searchpoint, vertex, c2);
flag = num > 0.0;
}
while (flag2)
{
searchtri.Oprev();
if (searchtri.tri.id == -1)
{
logger.Error("Unable to find a triangle on path.", "Mesh.FindDirection().2");
throw new Exception("Unable to find a triangle on path.");
}
c = searchtri.Dest();
num = num2;
num2 = predicates.CounterClockwise(vertex, searchpoint, c);
flag2 = num2 > 0.0;
}
if (num == 0.0)
{
return FindDirectionResult.Leftcollinear;
}
if (num2 == 0.0)
{
return FindDirectionResult.Rightcollinear;
}
return FindDirectionResult.Within;
}
private void SegmentIntersection(ref Otri splittri, ref Osub splitsubseg, Vertex endpoint2)
{
Osub os = default(Osub);
SubSegment dummysub = mesh.dummysub;
Vertex vertex = splittri.Apex();
Vertex vertex2 = splittri.Org();
Vertex vertex3 = splittri.Dest();
double num = vertex3.x - vertex2.x;
double num2 = vertex3.y - vertex2.y;
double num3 = endpoint2.x - vertex.x;
double num4 = endpoint2.y - vertex.y;
double num5 = vertex2.x - endpoint2.x;
double num6 = vertex2.y - endpoint2.y;
double num7 = num2 * num3 - num * num4;
if (num7 == 0.0)
{
logger.Error("Attempt to find intersection of parallel segments.", "Mesh.SegmentIntersection()");
throw new Exception("Attempt to find intersection of parallel segments.");
}
double num8 = (num4 * num5 - num3 * num6) / num7;
Vertex vertex4 = new Vertex(vertex2.x + num8 * (vertex3.x - vertex2.x), vertex2.y + num8 * (vertex3.y - vertex2.y), splitsubseg.seg.boundary);
vertex4.hash = mesh.hash_vtx++;
vertex4.id = vertex4.hash;
vertex4.z = vertex2.z + num8 * (vertex3.z - vertex2.z);
mesh.vertices.Add(vertex4.hash, vertex4);
if (mesh.InsertVertex(vertex4, ref splittri, ref splitsubseg, segmentflaws: false, triflaws: false) != InsertVertexResult.Successful)
{
logger.Error("Failure to split a segment.", "Mesh.SegmentIntersection()");
throw new Exception("Failure to split a segment.");
}
vertex4.tri = splittri;
if (mesh.steinerleft > 0)
{
mesh.steinerleft--;
}
splitsubseg.Sym();
splitsubseg.Pivot(ref os);
splitsubseg.Dissolve(dummysub);
os.Dissolve(dummysub);
do
{
splitsubseg.SetSegOrg(vertex4);
splitsubseg.Next();
}
while (splitsubseg.seg.hash != -1);
do
{
os.SetSegOrg(vertex4);
os.Next();
}
while (os.seg.hash != -1);
FindDirection(ref splittri, vertex);
Vertex vertex5 = splittri.Dest();
Vertex vertex6 = splittri.Apex();
if (vertex6.x == vertex.x && vertex6.y == vertex.y)
{
splittri.Onext();
}
else if (vertex5.x != vertex.x || vertex5.y != vertex.y)
{
logger.Error("Topological inconsistency after splitting a segment.", "Mesh.SegmentIntersection()");
throw new Exception("Topological inconsistency after splitting a segment.");
}
}
private bool ScoutSegment(ref Otri searchtri, Vertex endpoint2, int newmark)
{
Otri ot = default(Otri);
Osub os = default(Osub);
FindDirectionResult findDirectionResult = FindDirection(ref searchtri, endpoint2);
Vertex vertex = searchtri.Dest();
Vertex vertex2 = searchtri.Apex();
if ((vertex2.x == endpoint2.x && vertex2.y == endpoint2.y) || (vertex.x == endpoint2.x && vertex.y == endpoint2.y))
{
if (vertex2.x == endpoint2.x && vertex2.y == endpoint2.y)
{
searchtri.Lprev();
}
mesh.InsertSubseg(ref searchtri, newmark);
return true;
}
switch (findDirectionResult)
{
case FindDirectionResult.Leftcollinear:
searchtri.Lprev();
mesh.InsertSubseg(ref searchtri, newmark);
return ScoutSegment(ref searchtri, endpoint2, newmark);
case FindDirectionResult.Rightcollinear:
mesh.InsertSubseg(ref searchtri, newmark);
searchtri.Lnext();
return ScoutSegment(ref searchtri, endpoint2, newmark);
default:
searchtri.Lnext(ref ot);
ot.Pivot(ref os);
if (os.seg.hash == -1)
{
return false;
}
SegmentIntersection(ref ot, ref os, endpoint2);
ot.Copy(ref searchtri);
mesh.InsertSubseg(ref searchtri, newmark);
return ScoutSegment(ref searchtri, endpoint2, newmark);
}
}
private void DelaunayFixup(ref Otri fixuptri, bool leftside)
{
Otri ot = default(Otri);
Otri ot2 = default(Otri);
Osub os = default(Osub);
fixuptri.Lnext(ref ot);
ot.Sym(ref ot2);
if (ot2.tri.id == -1)
{
return;
}
ot.Pivot(ref os);
if (os.seg.hash != -1)
{
return;
}
Vertex vertex = ot.Apex();
Vertex vertex2 = ot.Org();
Vertex vertex3 = ot.Dest();
Vertex vertex4 = ot2.Apex();
if (leftside)
{
if (predicates.CounterClockwise(vertex, vertex2, vertex4) <= 0.0)
{
return;
}
}
else if (predicates.CounterClockwise(vertex4, vertex3, vertex) <= 0.0)
{
return;
}
if (!(predicates.CounterClockwise(vertex3, vertex2, vertex4) > 0.0) || !(predicates.InCircle(vertex2, vertex4, vertex3, vertex) <= 0.0))
{
mesh.Flip(ref ot);
fixuptri.Lprev();
DelaunayFixup(ref fixuptri, leftside);
DelaunayFixup(ref ot2, leftside);
}
}
private void ConstrainedEdge(ref Otri starttri, Vertex endpoint2, int newmark)
{
Otri ot = default(Otri);
Otri ot2 = default(Otri);
Osub os = default(Osub);
Vertex a = starttri.Org();
starttri.Lnext(ref ot);
mesh.Flip(ref ot);
bool flag = false;
bool flag2 = false;
do
{
Vertex vertex = ot.Org();
if (vertex.x == endpoint2.x && vertex.y == endpoint2.y)
{
ot.Oprev(ref ot2);
DelaunayFixup(ref ot, leftside: false);
DelaunayFixup(ref ot2, leftside: true);
flag2 = true;
continue;
}
double num = predicates.CounterClockwise(a, endpoint2, vertex);
if (num == 0.0)
{
flag = true;
ot.Oprev(ref ot2);
DelaunayFixup(ref ot, leftside: false);
DelaunayFixup(ref ot2, leftside: true);
flag2 = true;
continue;
}
if (num > 0.0)
{
ot.Oprev(ref ot2);
DelaunayFixup(ref ot2, leftside: true);
ot.Lprev();
}
else
{
DelaunayFixup(ref ot, leftside: false);
ot.Oprev();
}
ot.Pivot(ref os);
if (os.seg.hash == -1)
{
mesh.Flip(ref ot);
continue;
}
flag = true;
SegmentIntersection(ref ot, ref os, endpoint2);
flag2 = true;
}
while (!flag2);
mesh.InsertSubseg(ref ot, newmark);
if (flag && !ScoutSegment(ref ot, endpoint2, newmark))
{
ConstrainedEdge(ref ot, endpoint2, newmark);
}
}
private void InsertSegment(Vertex endpoint1, Vertex endpoint2, int newmark)
{
Otri otri = default(Otri);
Otri otri2 = default(Otri);
Vertex vertex = null;
Triangle dummytri = mesh.dummytri;
otri = endpoint1.tri;
if (otri.tri != null)
{
vertex = otri.Org();
}
if (vertex != endpoint1)
{
otri.tri = dummytri;
otri.orient = 0;
otri.Sym();
if (locator.Locate(endpoint1, ref otri) != LocateResult.OnVertex)
{
logger.Error("Unable to locate PSLG vertex in triangulation.", "Mesh.InsertSegment().1");
throw new Exception("Unable to locate PSLG vertex in triangulation.");
}
}
locator.Update(ref otri);
if (ScoutSegment(ref otri, endpoint2, newmark))
{
return;
}
endpoint1 = otri.Org();
vertex = null;
otri2 = endpoint2.tri;
if (otri2.tri != null)
{
vertex = otri2.Org();
}
if (vertex != endpoint2)
{
otri2.tri = dummytri;
otri2.orient = 0;
otri2.Sym();
if (locator.Locate(endpoint2, ref otri2) != LocateResult.OnVertex)
{
logger.Error("Unable to locate PSLG vertex in triangulation.", "Mesh.InsertSegment().2");
throw new Exception("Unable to locate PSLG vertex in triangulation.");
}
}
locator.Update(ref otri2);
if (!ScoutSegment(ref otri2, endpoint1, newmark))
{
endpoint2 = otri2.Org();
ConstrainedEdge(ref otri, endpoint2, newmark);
}
}
private void MarkHull()
{
Otri tri = default(Otri);
Otri ot = default(Otri);
Otri ot2 = default(Otri);
tri.tri = mesh.dummytri;
tri.orient = 0;
tri.Sym();
tri.Copy(ref ot2);
do
{
mesh.InsertSubseg(ref tri, 1);
tri.Lnext();
tri.Oprev(ref ot);
while (ot.tri.id != -1)
{
ot.Copy(ref tri);
tri.Oprev(ref ot);
}
}
while (!tri.Equals(ot2));
}
}
}