Skip to content

Commit 473a249

Browse files
Homer Reidstevengj
authored andcommitted
fix handling of normals to objects from points on prism faces (#14)
1 parent 69209a4 commit 473a249

2 files changed

Lines changed: 107 additions & 83 deletions

File tree

utils/geom.c

Lines changed: 29 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -2190,28 +2190,6 @@ boolean point_in_polygon(double px, double py, vector3 *vertices, int num_vertic
21902190
return num_side_intersections%2;
21912191
}
21922192

2193-
void add_to_list(double s, double *slist, int length)
2194-
{
2195-
switch(length)
2196-
{ case 0:
2197-
slist[0] = s;
2198-
break;
2199-
case 1:
2200-
if (s>=slist[0])
2201-
slist[1]=s;
2202-
else
2203-
{ slist[1]=slist[0]; slist[0]=s; }
2204-
break;
2205-
default:
2206-
if (s<slist[0])
2207-
{ slist[1]=slist[0]; slist[0]=s; }
2208-
else if (s<slist[1])
2209-
slist[1]=s;
2210-
break;
2211-
}
2212-
2213-
}
2214-
22152193
/***************************************************************/
22162194
/* return 1 or 0 if xc lies inside or outside the prism */
22172195
/***************************************************************/
@@ -2322,36 +2300,25 @@ double intersect_line_segment_with_prism(prism *prsm, vector3 p, vector3 d, doub
23222300
}
23232301

23242302
/***************************************************************/
2325-
/* compute the minimum-length vector from p to the plane */
2303+
/* compute the minimum distance from p to the plane */
23262304
/* containing o (origin) and spanned by basis vectors v1,v2 */
23272305
/* algorithm: solve the 3x3 system p-s*v3 = o + t*v1 + u*v2 */
2328-
/* where v3 = v1 x v2 and s,t,u are unknowns */
2306+
/* where v3 = v1 x v2 and s,t,u are unknowns. */
2307+
/* return value is unit normal vector to plane and *s is such */
2308+
/* that p-(*s)*v3 lies in the plane */
23292309
/***************************************************************/
2330-
vector3 normal_to_plane(vector3 o, vector3 v1, vector3 v2, vector3 p)
2310+
vector3 normal_to_plane(vector3 o, vector3 v1, vector3 v2, vector3 p, double *s)
23312311
{
2332-
vector3 RHS = vector3_minus(p,o);
2333-
2334-
// handle the degenerate-to-2D case
2335-
if ( (fabs(v1.z) + fabs(v2.z)) < 2.0e-7 && fabs(RHS.z)<1.0e-7 )
2336-
{ vector3 zhat={0,0,1};
2337-
vector3 v3 = vector3_cross(zhat, v1);
2338-
double M00 = v1.x; double M10 = v3.x;
2339-
double M01 = v1.y; double M11 = v3.y;
2340-
double DetM = M00*M11 - M01*M10;
2341-
if ( fabs(DetM) < 1.0e-10 )
2342-
return vector3_scale(0.0, v3);
2343-
// double t = (M00*RHSy-M10*RHSx)/DetM;
2344-
double s= (M11*RHS.x-M01*RHS.y)/DetM;
2345-
return vector3_scale(-1.0*s,v3);
2346-
}
2347-
2348-
vector3 v3 = vector3_cross(v1, v2);
2312+
vector3 v3 = unit_vector3(vector3_cross(v1, v2));
2313+
CHECK( (vector3_norm(v3)>1.0e-6), "degenerate plane in normal_to_plane" );
23492314
matrix3x3 M;
23502315
M.c0 = v1;
23512316
M.c1 = v2;
2352-
M.c2 = vector3_scale(-1.0, v3);
2353-
vector3 tus = matrix3x3_vector3_mult(matrix3x3_inverse(M),RHS);
2354-
return vector3_scale(-1.0*tus.z, v3);
2317+
M.c2 = v3;
2318+
vector3 RHS = vector3_minus(p,o);
2319+
vector3 tus = matrix3x3_vector3_mult(matrix3x3_inverse(M),RHS); // "t, u, s"
2320+
*s = tus.z;
2321+
return v3;
23552322
}
23562323

23572324
/***************************************************************/
@@ -2370,19 +2337,21 @@ vector3 normal_to_prism(prism *prsm, vector3 xc)
23702337
vector3 axis = {0,0,0}; axis.z=height;
23712338

23722339
vector3 retval;
2373-
double min_distance;
2340+
double min_distance=HUGE_VAL;
23742341

23752342
// side walls
2376-
int nv;
2377-
for(nv=0; nv<num_vertices; nv++)
2378-
{ int nvp1 = ( nv==(num_vertices-1) ? 0 : nv+1 );
2379-
vector3 v1 = vector3_minus(vertices[nvp1],vertices[nv]);
2380-
vector3 v2 = axis;
2381-
vector3 v3 = normal_to_plane(vertices[nv], v1, v2, xp);
2382-
double distance = vector3_norm(v3);
2383-
if (nv==0 || distance < min_distance)
2384-
{ min_distance = distance;
2385-
retval = v3;
2343+
if (height>0.0)
2344+
{ int nv;
2345+
for(nv=0; nv<num_vertices; nv++)
2346+
{ int nvp1 = ( nv==(num_vertices-1) ? 0 : nv+1 );
2347+
vector3 v1 = vector3_minus(vertices[nvp1],vertices[nv]);
2348+
vector3 v2 = axis;
2349+
double s;
2350+
vector3 v3 = normal_to_plane(vertices[nv], v1, v2, xp, &s);
2351+
if (fabs(s) < min_distance)
2352+
{ min_distance = fabs(s);
2353+
retval = v3;
2354+
}
23862355
}
23872356
}
23882357

@@ -2394,10 +2363,10 @@ vector3 normal_to_prism(prism *prsm, vector3 xc)
23942363
vector3 v2 = vector3_cross(zhat,v1);
23952364
vector3 o = {0,0,0};
23962365
if (UpperLower) o.z = height;
2397-
vector3 v3 = normal_to_plane(o, v1, v2, xp);
2398-
double distance = vector3_norm(v3);
2399-
if (distance < min_distance)
2400-
{ min_distance = distance;
2366+
double s;
2367+
vector3 v3 = normal_to_plane(o, v1, v2, xp, &s);
2368+
if (fabs(s) < min_distance)
2369+
{ min_distance = fabs(s);
24012370
retval = v3;
24022371
}
24032372
}

utils/test-prism.c

Lines changed: 78 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,6 @@
3333

3434
#define K_PI 3.141592653589793238462643383279502884197
3535

36-
#define NUMPTS 10000
37-
#define NUMLINES 1000
38-
39-
#define LX 0.5
40-
#define LY 1.0
41-
#define LZ 1.5
42-
4336
// routine from geom.c that rotates the coordinate of a point
4437
// from the prism coordinate system to the cartesian coordinate system
4538
vector3 prism_coordinate_p2c(prism *prsm, vector3 vp);
@@ -63,22 +56,69 @@ static double drand()
6356
/************************************************************************/
6457
/* random point uniformly distributed over a parallelepiped */
6558
/************************************************************************/
66-
vector3 random_point(vector3 min_corner, vector3 max_corner)
67-
{ return make_vector3( urand(min_corner.x, max_corner.x),
59+
vector3 random_point_in_box(vector3 min_corner, vector3 max_corner)
60+
{
61+
return make_vector3( urand(min_corner.x, max_corner.x),
6862
urand(min_corner.y, max_corner.y),
6963
urand(min_corner.z, max_corner.z)
7064
);
7165
}
7266

7367
/************************************************************************/
74-
/* random unit vector uniformly distributed over a sphere */
68+
/* random point uniformly distributed over a planar polygon */
69+
/* (all z coordinates are 0) */
70+
/************************************************************************/
71+
vector3 random_point_in_polygon(vector3 *vertices, int num_vertices)
72+
{
73+
// randomly choose a vertex and generate random point within the triangle
74+
// formed by that vertex, the next vertex, and the centroid
75+
int which_vertex = rand() % num_vertices;
76+
vector3 v0 = {0,0,0};
77+
vector3 v1 = vertices[which_vertex];
78+
vector3 v2 = vertices[(which_vertex+1)%num_vertices];
79+
double xi = urand(0.0,1.0), eta = urand(0.0,1.0-xi);
80+
return vector3_plus( vector3_scale(xi, vector3_minus(v1,v0)),
81+
vector3_scale(eta, vector3_minus(v2,v0))
82+
);
83+
}
84+
85+
86+
/************************************************************************/
87+
/* random point uniformly distributed over the surface of a prism */
88+
/************************************************************************/
89+
vector3 random_point_on_prism(geometric_object o)
90+
{
91+
prism *prsm = o.subclass.prism_data;
92+
vector3 *vertices = prsm->vertices.items;
93+
int num_vertices = prsm->vertices.num_items;
94+
double height = prsm->height;
95+
96+
// choose a face
97+
int num_faces = num_vertices + 2;
98+
int which_face = rand() % num_faces;
99+
if ( which_face < num_vertices ) // side face
100+
{ vector3 min_corner = vertices[which_face];
101+
vector3 max_corner = vertices[ (which_face+1)%num_vertices ];
102+
max_corner.z = height;
103+
return random_point_in_box( prism_coordinate_p2c(prsm, min_corner),
104+
prism_coordinate_p2c(prsm, max_corner) );
105+
}
106+
else // floor or ceiling
107+
{
108+
vector3 p = random_point_in_polygon(vertices, num_vertices);
109+
if (which_face==num_faces-1) p.z=height;
110+
return prism_coordinate_p2c(prsm, p);
111+
}
112+
}
113+
114+
/************************************************************************/
115+
/* random unit vector with direction uniformly distributed over unit sphere*/
75116
/************************************************************************/
76-
vector3 random_unit_vector3(void)
117+
vector3 random_unit_vector3()
77118
{
78-
double z = urand(-1.0,1.0);
79-
double rho = sqrt(1 - z*z);
80-
double phi = urand(0.0, 2.0*K_PI);
81-
return make_vector3( rho*cos(phi), rho*sin(phi), z );
119+
double cos_theta=urand(0.0,1.0), sin_theta=sqrt(1.0-cos_theta*cos_theta);
120+
double phi=urand(0.0,2.0*K_PI);
121+
return make_vector3(sin_theta*cos(phi), sin_theta*sin(phi), cos_theta);
82122
}
83123

84124
/***************************************************************/
@@ -169,7 +209,7 @@ int test_point_inclusion(geometric_object the_block, geometric_object the_prism,
169209
int n;
170210
for(n=0; n<num_tests; n++)
171211
{
172-
vector3 p = random_point(min_corner, max_corner);
212+
vector3 p = random_point_in_box(min_corner, max_corner);
173213
boolean in_block=point_in_objectp(p,the_block);
174214
boolean in_prism=point_in_objectp(p,the_prism);
175215
if (in_block!=in_prism)
@@ -185,6 +225,7 @@ int test_point_inclusion(geometric_object the_block, geometric_object the_prism,
185225
/************************************************************************/
186226
/* second unit test: check calculation of normals to objects */
187227
/************************************************************************/
228+
#define PFACE 0.1
188229
int test_normal_to_object(geometric_object the_block, geometric_object the_prism,
189230
int num_tests, int write_log)
190231
{
@@ -194,13 +235,18 @@ int test_normal_to_object(geometric_object the_block, geometric_object the_prism
194235
FILE *f = write_log ? fopen("/tmp/test-prism.normals","w") : 0;
195236

196237
int num_failed=0;
197-
int n;
198238
double tolerance=1.0e-6;
239+
240+
int n;
199241
for(n=0; n<num_tests; n++)
200242
{
201-
// randomly generated base point within enlarged bounding box
202-
vector3 p = random_point(min_corner, max_corner);
203-
243+
// with probability PFACE, generate random base point lying on one
244+
// of the 6 faces of the prism.
245+
// with probability 1-PFACE, generate random base point lying in the
246+
// extended volume (2x volume of block)
247+
vector3 p = ( urand(0.0,1.0) < PFACE) ? random_point_on_prism(the_prism)
248+
: random_point_in_box(min_corner, max_corner);
249+
204250
vector3 nhat_block=standardize(normal_to_object(p, the_block));
205251
vector3 nhat_prism=standardize(normal_to_object(p, the_prism));
206252
if (!vector3_nearly_equal(nhat_block, nhat_prism, tolerance))
@@ -234,7 +280,7 @@ int test_line_segment_intersection(geometric_object the_block, geometric_object
234280
for(n=0; n<num_tests; n++)
235281
{
236282
// randomly generated base point within enlarged bounding box
237-
vector3 p = random_point(min_corner, max_corner);
283+
vector3 p = random_point_in_box(min_corner, max_corner);
238284
vector3 d = random_unit_vector3();
239285
double a = urand(0.0,1.0);
240286
double b = urand(0.0,1.0);
@@ -258,6 +304,13 @@ int test_line_segment_intersection(geometric_object the_block, geometric_object
258304
/* block and as a prism) and verify that geometric primitives */
259305
/* give identical results */
260306
/***************************************************************/
307+
#define NUMPTS 10000
308+
#define NUMLINES 1000
309+
310+
#define LX 0.5
311+
#define LY 1.0
312+
#define LZ 1.5
313+
261314
int run_unit_tests()
262315
{
263316
void* m = NULL;
@@ -266,16 +319,18 @@ int run_unit_tests()
266319
vector3 yhat = make_vector3(0,1,0);
267320
vector3 zhat = make_vector3(0,0,1);
268321
vector3 size = make_vector3(LX,LY,LZ);
269-
geometric_object the_block = make_block(m, c, xhat, yhat, zhat, size);
270322

271323
vector3 v[4];
272324
v[0].x=-0.5*LX; v[0].y=-0.5*LY; v[0].z=-0.5*LZ;
273325
v[1].x=+0.5*LX; v[1].y=-0.5*LY; v[1].z=-0.5*LZ;
274326
v[2].x=+0.5*LX; v[2].y=+0.5*LY; v[2].z=-0.5*LZ;
275327
v[3].x=-0.5*LX; v[3].y=+0.5*LY; v[3].z=-0.5*LZ;
328+
329+
geometric_object the_block = make_block(m, c, xhat, yhat, zhat, size);
276330
geometric_object the_prism=make_prism(m, v, 4, LZ, zhat);
277331

278-
int write_log=0;
332+
char *s=getenv("LIBCTL_TEST_PRISM_LOG");
333+
int write_log = (s && s[0]=='1') ? 1 : 0;
279334

280335
if (write_log)
281336
prism2gnuplot(the_prism.subclass.prism_data, "/tmp/test-prism.prism");

0 commit comments

Comments
 (0)