-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlevelset.cs
More file actions
156 lines (137 loc) · 5.05 KB
/
levelset.cs
File metadata and controls
156 lines (137 loc) · 5.05 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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
using System;
using System.Linq;
using static Godot.GD;
using static VectorFuncs;
using NumFlat;
public class LevelSet {
public Vec<float> distance_field;
public int width, height;
public LevelSet(int width, int height) {
this.width = width;
this.height = height;
distance_field = new Vec<float>(width*height);
Clear();
}
public void Clear() {
distance_field.Fill(float.MaxValue);
}
public Vec<float> GetField() {
return distance_field;
}
public float GetDistance(Vector2 position) {
return VectorFuncs.LerpFloatField(position,distance_field,width,height);
}
public float GetDistance(float x, float y) {
return VectorFuncs.LerpFloatField(x,y,distance_field,width,height);
}
public float GetDistanceOnGrid(int x, int y) {
if (x < 0 || y < 0 || x >= width || y >= height) {
return -1; // in solid
}
return distance_field[x*width+y];
}
public float GetDistanceOnGrid(int position) {
return distance_field.Memory.Span[position];
}
public void AddBox(float x0, float y0, float x1, float y1, float sign = 1f) {
UnionBox(Math.Min(x0,x1),Math.Min(y0,y1),Math.Max(x0,x1),Math.Max(y0,y1),sign);
}
public void UnionBox(float x0, float y0, float x1, float y1, float sign = 1f) {
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
int position = i*width + j;
// if i,j is in box
if (x0 < i && i < x1 && y0 < j && j < y1) {
distance_field[position] = sign * new float[] { x0-i, i-x1, y0-j, j-y1, -Math.Abs(distance_field[position])}.Max();
}
else {
float p,q;
if (i < x0) {
p = x0;
} else if (i > x1) {
p = x1;
}
else {
p = (float)(i);
}
if (j < y0) {
q = y0;
} else if (j > y1) {
q = y1;
}
else {
q = (float)(j);
}
//make negative if already negative
if (distance_field[position] < 0 || sign < 0) {
distance_field[position] = (float)Math.Max(-1.0f * Math.Sqrt((i-p)*(i-p) + (j-q)*(j-q)),-Math.Abs(distance_field[position]));
}
else {
distance_field[position] = (float)Math.Min(Math.Sqrt((i-p)*(i-p) + (j-q)*(j-q)),distance_field[position]);
}
}
}
}
}
public Vector2 FindClosest(Vector2 position) {
int N = 3;
int M = 3;
float epsilon = 0.00001f;
Vector2 p = position;
p.Constrain(0,0,width-1,height-1,1);
float distance = VectorFuncs.LerpFloatField(p,distance_field,width,height);
Vector2 dir = VectorFuncs.GradientFloatField(p,distance_field,1,width,height);
if (Math.Abs(distance) < epsilon) {
return p;
}
for (int i = 0; i < N; i++) {
float alpha = 1;
for (int j = 0; j < M; j++) {
Vector2 q = p - alpha * distance * dir;
float q_distance = VectorFuncs.LerpFloatField(q, distance_field,width,height);
if (Math.Abs(q_distance) < Math.Abs(distance)) {
p = q;
distance = q_distance;
dir = VectorFuncs.GradientFloatField(q,distance_field,1,width,height);
if (Math.Abs(q_distance) < epsilon) {
return p;
}
}
else {
alpha *= 0.7f;
}
}
}
return p;
}
public Vector2 FindClosest(float x, float y) {
int N = 3;
int M = 3;
float epsilon = 0.001f;
Vector2 p = new Vector2(x,y);
float distance = VectorFuncs.LerpFloatField(p, distance_field,width,height);
Vector2 dir = VectorFuncs.GradientFloatField(p,distance_field,1,width,height);
if (Math.Abs(distance) < epsilon) {
return p;
}
for (int i = 0; i < N; i++) {
float alpha = 1;
for (int j = 0; j < M; j++) {
Vector2 q = p - alpha * distance * dir;
float q_distance = VectorFuncs.LerpFloatField(q,distance_field,width,height);
if (Math.Abs(q_distance) < Math.Abs(distance)) {
p = q;
distance = q_distance;
dir = VectorFuncs.GradientFloatField(q,distance_field,1,width,height);
if (Math.Abs(q_distance) < epsilon) {
return p;
}
}
else {
alpha *= 0.7f;
}
}
}
return p;
}
}