-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathPolyline.Reduction.cs
More file actions
104 lines (95 loc) · 3.93 KB
/
Polyline.Reduction.cs
File metadata and controls
104 lines (95 loc) · 3.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
using System.Numerics;
using MeshWiz.Collections;
using MeshWiz.Utility;
namespace MeshWiz.Math;
public static partial class Polyline
{
public static class Reduction
{
public static Polyline<TVec, TNum> DouglasPeucker<TVec, TNum>(Polyline<TVec, TNum> polyline, TNum eps=default)
where TVec : unmanaged, IVec<TVec, TNum>
where TNum : unmanaged, IFloatingPointIeee754<TNum>
{
if (eps == default)
eps = Numbers<TNum>.ZeroEpsilon;
var epsilon = TNum.Abs(eps);
if (epsilon < TNum.Epsilon) return polyline;
var ptSpan = polyline.Points;
if (ptSpan.Length < 3) return polyline;
var keep = new bool[ptSpan.Length];
keep[0] = true;
keep[^1] = true;
var keepCount = 2;
RollingList<Range> jobs = [Range.All];
epsilon*=epsilon; //squared for faster comparisons
while (jobs.TryPopFront(out var range))
{
var (start, length) = range.GetOffsetAndLength(ptSpan.Length);
var end = start + length - 1;
var l = ptSpan[start].LineTo(ptSpan[end]);
var max = TNum.NegativeInfinity;
var maxPos = -1;
for (var i = start + 1; i < end; i++)
{
var p = ptSpan[i];
var d = l.SquaredDistanceTo(p);
if (d < max) continue;
max = d;
maxPos = i;
}
if(max<epsilon||maxPos==-1) continue;
keep[maxPos] = true;
keepCount++;
if (maxPos - start > 1) jobs.PushFront(start..(maxPos+1));
if (end - maxPos > 1) jobs.PushFront(maxPos..(end+1));
}
var result = new TVec[keepCount];
keepCount = -1;
for (var i = 0; i < keep.Length; i++)
if (keep[i]) result[++keepCount] = ptSpan[i];
return new Polyline<TVec, TNum>(result);
}
public static TPos[] DouglasPeucker<TPos,TVector, TNum>(ReadOnlySpan<TPos> ptSpan, TNum? eps=null)
where TPos : unmanaged, IPosition<TPos,TVector, TNum>
where TNum : unmanaged, IFloatingPointIeee754<TNum>
where TVector : unmanaged, IVec<TVector, TNum>
{
eps ??= Numbers<TNum>.ZeroEpsilon;
var epsilon = TNum.Abs(eps.Value);
if (epsilon < TNum.Epsilon) return ptSpan.ToArray();
if (ptSpan.Length < 3) return ptSpan.ToArray();
var keep = new bool[ptSpan.Length];
keep[0] = true;
keep[^1] = true;
var keepCount = 2;
RollingList<Range> jobs = [Range.All];
epsilon*=epsilon; //squared for faster comparisons
while (jobs.TryPopFront(out var range))
{
var (start, length) = range.GetOffsetAndLength(ptSpan.Length);
var end = start + length - 1;
var l = ptSpan[start].Position.LineTo(ptSpan[end].Position);
var max = TNum.NegativeInfinity;
var maxPos = -1;
for (var i = start + 1; i < end; i++)
{
var p = ptSpan[i];
var d = l.SquaredDistanceTo(p.Position);
if (d < max) continue;
max = d;
maxPos = i;
}
if(max<epsilon||maxPos==-1) continue;
keep[maxPos] = true;
keepCount++;
if (maxPos - start > 1) jobs.PushFront(start..(maxPos+1));
if (end - maxPos > 1) jobs.PushFront(maxPos..(end+1));
}
var result = new TPos[keepCount];
keepCount = -1;
for (var i = 0; i < keep.Length; i++)
if (keep[i]) result[++keepCount] = ptSpan[i];
return result;
}
}
}