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