1 #include <cstdlib>
2 #include <cmath>
3 #include "boxm2_vecf_mouth.h"
4
boxm2_vecf_mouth(std::vector<vgl_point_3d<double>> const & knots)5 boxm2_vecf_mouth::boxm2_vecf_mouth(std::vector<vgl_point_3d<double> > const& knots){
6 sup_ = bvgl_spline_region_3d<double>(knots, 1.0);
7 inf_ = bvgl_spline_region_3d<double>(knots, 1.0);
8 // set the sense of the normals so that points inside the mouth
9 // have positive signed distances from both region planes
10 vgl_point_3d<double> cent_sup = sup_.centroid();
11 vgl_point_3d<double> cent_inf = cent_sup;
12 cent_sup.set(cent_sup.x(), cent_sup.y()-1.0, cent_sup.z());
13 cent_inf.set(cent_inf.x(), cent_inf.y()+1.0, cent_inf.z());
14 sup_.set_point_positive(cent_sup);
15 inf_.set_point_positive(cent_inf);
16 }
17
boxm2_vecf_mouth(vgl_pointset_3d<double> const & ptset)18 boxm2_vecf_mouth::boxm2_vecf_mouth(vgl_pointset_3d<double> const& ptset){
19 *this = boxm2_vecf_mouth(ptset.points());
20 }
21 //
22 // rotate the plane of the lower lip accoding to the
23 // angle of the mandible. Defines the extreme lower bound
24 // on the mouth region
25 //
rotate_inf()26 void boxm2_vecf_mouth::rotate_inf(){
27 std::vector<vgl_point_3d<double> > knots = sup_.knots();
28 // rotate the knots of the sup rather than current inf knots
29 // thus an invariant starting point before rotation
30 std::vector<vgl_point_3d<double> > rot_knots;
31 for(auto & knot : knots){
32 vgl_point_3d<double> rp = rot_*knot;
33 rot_knots.push_back(rp);
34 }
35 inf_ = bvgl_spline_region_3d<double>(rot_knots, 1.0);
36 vgl_point_3d<double> cent_sup = sup_.centroid();
37 // add to y to avoid degenerate offset above inf
38 cent_sup.set(cent_sup.x(), cent_sup.y()+0.1, cent_sup.z());
39 inf_.set_point_positive(cent_sup);
40 }
41
set_mandible_params(boxm2_vecf_mandible_params const & mand_params)42 void boxm2_vecf_mouth::set_mandible_params(boxm2_vecf_mandible_params const& mand_params){
43 mand_params_ = mand_params;
44 vnl_vector_fixed<double,3> X(1.0, 0.0, 0.0);
45 vnl_quaternion<double> Q(X,mand_params_.jaw_opening_angle_rad_);
46 rot_ = vgl_rotation_3d<double>(Q);
47 this->rotate_inf();
48 vgl_box_3d<double> bb = this->bounding_box();
49 params_.t_max_= params_.t(0.0, bb.min_y());
50 }
51
bounding_box() const52 vgl_box_3d<double> boxm2_vecf_mouth::bounding_box() const{
53 std::vector<vgl_point_3d<double> > sup_knots = sup_.knots();
54 std::vector<vgl_point_3d<double> > inf_knots = inf_.knots();
55 vgl_box_3d<double> ret;
56 for(auto & sup_knot : sup_knots)
57 ret.add(sup_knot);
58 for(auto & inf_knot : inf_knots)
59 ret.add(inf_knot);
60 return ret;
61 }
62 //
63 // the shape of the mouth opening is controlled mainly by the
64 // orbicularis oris muscle. The shape of the opening is defined
65 // by a linear blend of two polynomials by the parameter t
66 // this function tests to see if the x,y position of a point
67 // lies inside the region defined by the two polynomials. The
68 // lower lip t = t_max is determined from the rotation angle of the mandible
69 // the upper lip t is defined to be zero. The x,y coordinates determine
70 // a t value so that the point lies on the blended polynomial. If
71 // t lies inside the bounds then the point is in the mouth opening.
72 //
in_oris(vgl_point_3d<double> const & pt) const73 bool boxm2_vecf_mouth::in_oris(vgl_point_3d<double> const& pt) const{
74 double xp = 0.95*pt.x(), y = pt.y();
75 if(xp<params_.x_min_ || xp>params_.x_max_)
76 return false;
77 double t = params_.t(xp, y);
78 bool valid = valid_t(t);
79 return valid;
80 }
81 // the idea here is to map the point, pt, backwards using a linear
82 // approximation to the rotational vector field of the mandible,
83 // that is,
84 // [ 1 0 0 ]
85 // R_inv(theta) = [ 0 1 theta ]
86 // [ 0 -theta 1 ]
87 //
88 // the value of theta needed to map the point back to sup,
89 // (the mouth plane when theta == 0) can be found by solving the
90 // linear equation:
91 //
92 // { a b c d][ ][ x ]
93 // [ R(theta)][ y ] = 0
94 // [ ][ z ]
95 // [ ][ 1 ]
96 // The solution is,
97 //
98 // theta = -( ax + by +cz +d)/(bz -cy)
99 //
100 // The plane is normalized below to insure good numerical conditioning
101 // for the linear solution. Note that b ~ 1.0 and |c| << |b| so the
102 // singular condition (bz - cy) == 0 occurs in plane, -cy + bz = 0,
103 // approximately the z=0 plane passing through the x axis.
104 //
in(vgl_point_3d<double> const & pt) const105 bool boxm2_vecf_mouth::in(vgl_point_3d<double> const& pt) const{
106 double x = pt.x(), y = pt.y(), z = pt.z();
107 const vgl_plane_3d<double>& plane = sup_.plane();
108 double a = plane.a(), b = plane.b(), c = plane.c(), d = plane.d();
109 double norm = std::sqrt(a*a + b*b + c*c);
110 a/=norm; b/=norm; c/=norm; d/=norm;
111 double theta = -(a*x + b*y + c*z + d)/(b*z - c*y);
112 double theta_max = mand_params_.jaw_opening_angle_rad_;
113 if(theta<=0.0 || theta>theta_max)
114 return false;
115 vgl_point_3d<double> inv_p(x, (y+theta*z), (z-theta*y));
116 bool in = sup_.in(inv_p);
117 if(in){
118 bool in_o = this->in_oris(pt);
119 return in_o;
120 }
121 return false;
122 }
123 //
124 // A randomly generated set of 3-d points that lie inside the mouth for debugging purposes
125 //
random_pointset(unsigned n_pts) const126 vgl_pointset_3d<double> boxm2_vecf_mouth::random_pointset(unsigned n_pts) const{
127 // add sup and inf random pointsets
128 vgl_pointset_3d<double> pts = sup_.random_pointset(n_pts);
129 vgl_pointset_3d<double> inf_pts = inf_.random_pointset(n_pts);
130 pts.append_pointset(inf_pts);
131 vgl_box_3d<double> bb = this->bounding_box();
132 // generate interior points
133 unsigned n_req = 100*n_pts, niter =0;
134 double xmin = bb.min_x(), xmax = bb.max_x();
135 double ymin = bb.min_y(), ymax = bb.max_y();
136 double zmin = bb.min_z(), zmax = bb.max_z();
137 while(n_req>0 && niter < 10000*n_pts){
138 double x = (xmax-xmin)*(static_cast<double>(std::rand())/static_cast<double>(RAND_MAX)) + xmin;
139 double y = (ymax-ymin)*(static_cast<double>(std::rand())/static_cast<double>(RAND_MAX)) + ymin;
140 double z = (zmax-zmin)*(static_cast<double>(std::rand())/static_cast<double>(RAND_MAX)) + zmin;
141 vgl_point_3d<double> p(x, y, z);
142 if(this->in(p)){
143 if(p.x()>0.0)
144 pts.add_point_with_normal(p, vgl_vector_3d<double>(-1.0, 0.0, 0.0));
145 else
146 pts.add_point_with_normal(p, vgl_vector_3d<double>(-1.0, 0.0, 0.0));
147 n_req--;
148 }else niter++;
149 }
150 if(n_req !=0)
151 std::cout << "Warning! Insufficient number of points " << pts.npts() << " instead of " << n_pts << '\n';
152
153 return pts;
154 }
155