-
Notifications
You must be signed in to change notification settings - Fork 9
/
levelset.cpp
151 lines (135 loc) · 5.29 KB
/
levelset.cpp
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
#include "levelset.h"
#include "util.h"
#include "array2_utils.h"
float interpolate_phi(const Vec2f& point, const Array2f& grid, const Vec2f& origin, const float dx) {
float inv_dx = 1/dx;
Vec2f temp = (point-origin)*inv_dx;
return interpolate_value(temp, grid);
}
float interpolate_normal(Vec2f& normal, const Vec2f& point, const Array2f& grid, const Vec2f& origin, const float dx) {
float inv_dx = 1/dx;
Vec2f temp = (point-origin)*inv_dx;
float value = interpolate_gradient(normal, temp, grid);
if(mag(normal) != 0)
normalize(normal);
return value;
}
void project_to_isosurface(Vec2f& point, const float target_value, const Array2f& grid, const Vec2f& origin, const float dx) {
float tol = 0.01f*dx; //some fraction of a grid cell;
int max_iter = 5;
int iter = 0;
Vec2f normal;
float phi = interpolate_normal(normal, point, grid, origin, dx);
while(fabs(phi - target_value) > tol && iter++ < max_iter) {
point -= (phi - target_value) * normal;
phi = interpolate_normal(normal, point, grid, origin, dx);
}
}
//On a signed distance field, compute the fraction inside the (negative) isosurface
//along the 1D line segment joining phi_left and phi_right sample points.
float fraction_inside(float phi_left, float phi_right)
{
//Note: should not generate divide by zero, because if
//signs are different, and both values are != 0,
//abs(left - right) is necessarily >= left, or right, alone, ie. not 0
return
(phi_left >= 0 && phi_right >= 0)? //all empty
0.0f :
( (phi_left < 0 && phi_right < 0)? //all full
1.0f:
(
(phi_left >= 0)?
(1 - phi_left / (phi_left - phi_right)): //right inside
(phi_left / (phi_left - phi_right)) //left inside
)
);
}
//On a signed distance field, compute the fraction inside the (negative) isosurface
//along the 1D line segment joining phi_left and phi_right sample points.
//Except now there are two level sets, and we want the fraction that is the union
//of their interiors
float fraction_inside_either(float phi_left0, float phi_right0, float phi_left1, float phi_right1)
{
if(phi_left0 <= 0) {
if(phi_right0 <= 0) {
//entirely inside solid0 [-/-][?]
return 1;
}
else { //phi_right0 > 0
if(phi_left1 <= 0) {
if(phi_right1 <= 0) {
//entirely inside solid1 -> [-/+][-/-]
return 1;
}
else {//both left sides are inside, neither right side [-/+][-/+]
return max( fraction_inside(phi_left0, phi_right0),
fraction_inside(phi_left1, phi_right1) );
}
}
else { //phi_left1 > 0
if(phi_right1 <= 0) { //opposite sides are interior [-/+][+/-]
float frac0 = fraction_inside(phi_left0, phi_right0);
float frac1 = fraction_inside(phi_left1, phi_right1);
float total = frac0+frac1;
if(total <= 1)
return total;
else
return 1;
}
else {//phi_right1 > 0
//phi1 plays no role, both outside [-/+][+/+]
return fraction_inside(phi_left0, phi_right0);
}
}
}
}
else {
if(phi_right0 <= 0) {
if(phi_left1 <= 0) {
if(phi_right1 <= 0) {
//entirely inside solid1[+/-][-/-]
return 1;
}
else {
//coming in from opposing sides [+/-][-/+]
float frac0 = fraction_inside(phi_left0, phi_right0);
float frac1 = fraction_inside(phi_left1, phi_right1);
float total = frac0+frac1;
if(total <= 1)
return total;
else
return 1;
}
}
else { //phi_left1 > 0
if(phi_right1 <= 0) {
//coming from the same side, take the larger one [+/-][+/-]
return max( fraction_inside(phi_left0, phi_right0),
fraction_inside(phi_left1, phi_right1) );
}
else { //phi_right > 0
//Only have to worry about phi_0 [+/-][+/+]
return fraction_inside(phi_left0, phi_right0);
}
}
}
else {
//Only have to worry about phi_1 [+/+][?]
return fraction_inside(phi_left1, phi_right1);
}
}
}
void compute_volume_fractions(const Array2f& levelset, const Vec2f& ls_origin, float ls_dx, Array2f& volumes, const Vec2f& v_origin, float v_dx, int subdivisions) {
float sub_dx = v_dx / (float)(subdivisions+1);
for(int j = 0; j < volumes.nj; ++j) for(int i = 0; i < volumes.ni; ++i) {
//centre of the volume cells
Vec2f bottom_left = v_origin + Vec2f(i*v_dx, j*v_dx);
int inside_samples = 0;
for(int subj = 0; subj < subdivisions+1; ++subj) for(int subi = 0; subi < subdivisions+1; ++subi) {
Vec2f point = bottom_left + Vec2f( (subi+0.5f)*sub_dx, (subj+0.5f)*sub_dx);
float data = interpolate_phi(point, levelset, ls_origin, ls_dx);
inside_samples += (data < 0)?1:0;
}
volumes(i,j) = (float)inside_samples / (float)sqr(subdivisions+1);
}
}