Program Listing for File rib.cc¶
↰ Return to documentation for file (src/rib.cc
)
/*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* The ENVE project *
* *
* Copyright (c) 2020, Davide Stocco and Enrico Bertolazzi. *
* *
* The ENVE project and its components are supplied under the terms of *
* the open source BSD 3-Clause License. The contents of the ENVE *
* project and its components may not be copied or disclosed except in *
* accordance with the terms of the BSD 3-Clause License. *
* *
* URL: https://opensource.org/licenses/BSD-3-Clause *
* *
* Davide Stocco *
* Department of Industrial Engineering *
* University of Trento *
* e-mail: davide.stocco@unitn.it *
* *
* Enrico Bertolazzi *
* Department of Industrial Engineering *
* University of Trento *
* e-mail: enrico.bertolazzi@unitn.it *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
#ifndef DOXYGEN_SHOULD_SKIP_THIS
#include "enve.hh"
using namespace acme;
namespace enve
{
/*\
| _ _
| _ __(_) |__
| | '__| | '_ \
| | | | | |_) |
| |_| |_|_.__/
|
\*/
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
rib::rib(void)
: disk(NAN_DISK),
m_id(integer(0)),
m_y(QUIET_NAN),
m_width(QUIET_NAN),
m_angle(QUIET_NAN)
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
rib::rib(
integer id,
real radius,
real y,
real width,
real angle
)
: disk(radius, point(0.0, y, 0.0), UNITY_VEC3),
m_id(id),
m_y(y),
m_width(width),
m_angle(angle)
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void
rib::copy(
rib const & rib_obj
)
{
this->disk::operator=(rib_obj);
this->m_id = rib_obj.m_id;
this->m_width = rib_obj.m_width;
this->m_angle = rib_obj.m_angle;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
real const &
rib::width(void)
const
{
return this->m_width;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
real const &
rib::angle(void)
const
{
return this->m_angle;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
real &
rib::width(void)
{
return this->m_width;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
real &
rib::angle(void)
{
return this->m_angle;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool
rib::isApprox(
rib const & rib_obj,
real tolerance
)
const
{
return this->disk::isApprox(rib_obj, tolerance) &&
IsApprox(this->m_id, rib_obj.m_id, tolerance) &&
IsApprox(this->m_y, rib_obj.m_y, tolerance) &&
IsApprox(this->m_width, rib_obj.m_width, tolerance) &&
IsApprox(this->m_angle, rib_obj.m_angle, tolerance);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool
rib::envelop(
triangleground::vecptr const & ground,
affine const & pose,
std::string const method,
output & out
)
const
{
#define CMD "enve::rib::envelop(...): "
if (method == "geometric")
{return this->envelopGeometric(ground, pose, out);}
else if (method == "sampling")
{return this->envelopSampling(ground, pose, out);}
else
{ENVE_ERROR(CMD "invalid enveloping method.");}
#undef CMD
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool
rib::envelop(
ground::flat const & ground,
affine const & pose,
std::string const method,
output & out
)
const
{
#define CMD "enve::rib::envelop(...): "
if (method == "geometric")
{return this->envelopGeometric(ground, pose, out);}
else if (method == "sampling")
{return this->envelopSampling(ground, pose, out);}
else
{ENVE_ERROR(CMD "invalid enveloping method.");}
#undef CMD
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool
rib::envelop(
affine const & pose,
output & out
)
const
{
out.point = pose.translation() + pose.linear() *
(this->center() - this->radius() * UNITZ_VEC3);
out.normal = pose.linear() * UNITZ_VEC3;
out.friction = real(0.0);
out.depth = real(0.0);
out.area = real(0.0);
out.volume = real(0.0);
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool
rib::envelopGeometric(
triangleground::vecptr const & ground,
affine const & pose,
output & out
)
const
{
// Rib alias variables
real const & radius = this->radius();
real const & width = this->m_width;
point const & center = this->center();
// Compute rib pose
point origin(pose.translation());
mat3 rotation(pose.linear());
mat3 rotation_inv(rotation.transpose());
vec3 normal_grd(rotation * this->normal());
point center_grd(origin + rotation * center);
// Store temporaries
vec3 normal_tmp;
point p_a, p_b;
real t_a, t_b, t_c, t_d, r_a, r_b, r_c, cos_t_c, sin_t_c,
section_area, segment_length_tmp, segment_area_tmp,
segment_area_tot, segment_volume_tmp, segment_volume_tot;
segment_area_tot = real(0.0);
segment_volume_tot = real(0.0);
point contact_point_tmp(ZEROS_VEC3);
vec3 contact_normal_tmp(ZEROS_VEC3);
real contact_friction_tmp = real(0.0);
point contact_point_tot(ZEROS_VEC3);
vec3 contact_normal_tot(ZEROS_VEC3);
real contact_friction_tot = real(0.0);
// Compute remaining contact parameters
bool int_bool = false;
segment segment_tmp;
disk rib_grd(radius, center_grd, normal_grd);
for (size_t i = 0; i < ground.size(); ++i)
{
// Perform rib/triangleground intersection
if (Intersection(*ground[i], rib_grd, segment_tmp, real(1e-5)))
{
// Find intersection points
p_a = rotation_inv * (segment_tmp.vertex(0) - center_grd);
p_b = rotation_inv * (segment_tmp.vertex(1) - center_grd);
// Find angles
t_a = std::atan2(p_a.z(), p_a.x());
t_b = std::atan2(p_b.z(), p_b.x());
// Check consistency
if(t_b < t_a)
{
std::swap(t_a, t_b);
std::swap(r_a, r_b);
}
t_c = (t_a + t_b) / real(2.0);
t_d = (t_b - t_a) / real(2.0);
// Check intersection
segment_length_tmp = segment_tmp.length();
if (t_b - t_a < EPSILON_LOW && segment_length_tmp < EPSILON_LOW)
{break;}
else
{int_bool = true;}
// Find radius
r_a = std::min(radius, p_a.norm());
r_b = std::min(radius, p_b.norm());
r_c = std::min(radius, std::max(real(0.0),
real(2.0) * r_a * r_b / std::max(EPSILON_MEDIUM, r_a + r_b) * std::cos(t_d)
));
// Find area
cos_t_c = std::cos(t_c);
sin_t_c = std::sin(t_c);
section_area = radius * radius * t_d - r_a * r_b * std::sin(t_b - t_a) / real(2.0);
// Store temporary results
segment_area_tmp = segment_length_tmp * width;
segment_volume_tmp = section_area * width;
contact_point_tmp = origin + rotation * (center + r_c * point(cos_t_c, real(0.0), sin_t_c));
normal_tmp = rotation * vec3(-cos_t_c, real(0.0), -sin_t_c);
contact_normal_tmp = (normal_tmp + normal_grd * (ground[i]->normal() - normal_tmp).dot(normal_grd)).normalized();
contact_friction_tmp = ground[i]->friction();
// Store total results
segment_area_tot += segment_area_tmp;
segment_volume_tot += segment_volume_tmp;
contact_point_tot += segment_volume_tmp * contact_point_tmp;
contact_normal_tot += segment_volume_tmp * contact_normal_tmp;
contact_friction_tot += segment_volume_tmp * contact_friction_tmp;
}
}
// Store output
if (int_bool && (segment_area_tot > EPSILON_MEDIUM || segment_volume_tot > EPSILON_MEDIUM))
{
out.point = contact_point_tot / segment_volume_tot;
out.normal = (contact_normal_tot / segment_volume_tot).normalized();
out.friction = contact_friction_tot / segment_volume_tot;
out.depth = radius - (out.point - center_grd).norm();
out.area = segment_area_tot;
out.volume = segment_volume_tot;
return true;
}
else
{
// No contact with the ground
this->envelop(pose, out);
return false;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool
rib::envelopGeometric(
ground::flat const & ground,
affine const & pose,
output & out
)
const
{
// Rib alias variables
real const & radius = this->radius();
real const & width = this->m_width;
point const & center = this->center();
// Compute rib pose
point origin(pose.translation());
mat3 rotation(pose.linear());
vec3 normal_grd(rotation * this->normal());
point center_grd(origin + rotation * center);
// Perform rib/flat intersection
vec3 normal_tmp;
segment segment_tmp;
disk rib_grd(radius, center_grd, normal_grd);
bool int_bool = Intersection(ground, rib_grd, segment_tmp, EPSILON_HIGH);
// Compute remaining contact parameters
if (int_bool && segment_tmp.length() > EPSILON_LOW)
{
out.point = segment_tmp.centroid();
normal_tmp = (normal_grd.cross(center_grd - out.point)).normalized();
out.normal = (ground.normal() - normal_tmp * ground.normal().dot(normal_tmp)).normalized();
out.friction = ground.friction();
out.depth = radius - (out.point - center_grd).norm();
out.area = real(2.0) * std::sqrt(out.depth * (real(2.0) * radius - out.depth)) * width;
out.volume = (radius * radius * std::acos((radius - out.depth) / radius) -
(radius - out.depth) * std::sqrt(out.depth * (real(2.0) * radius - out.depth))) * width;
return true;
}
else
{
// No contact with the ground
this->envelop(pose, out);
return false;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool
rib::envelopSampling(
triangleground::vecptr const & ground,
affine const & pose,
output & out
)
const
{
// Rib alias variables
real const & radius = this->radius();
real const & width = this->m_width;
point const & center = this->center();
// Compute rib pose
point origin(pose.translation());
mat3 rotation(pose.linear());
vec3 normal_grd(rotation * this->normal());
point center_grd(origin + rotation * center);
// Store temporaries
real delta_x = real(0.1) * radius;
real delta_y = real(0.3) * width;
std::vector<line> line_vec(4);
std::vector<point> point_vec(4);
std::vector<real> friction_vec(4);
// Build sampling lines
line_vec[0] = line(origin + rotation * (center + delta_x * UNITX_VEC3), -UNITZ_VEC3);
line_vec[1] = line(origin + rotation * (center - delta_x * UNITX_VEC3), -UNITZ_VEC3);
line_vec[2] = line(origin + rotation * (center + delta_y * UNITY_VEC3), -UNITZ_VEC3);
line_vec[3] = line(origin + rotation * (center - delta_y * UNITY_VEC3), -UNITZ_VEC3);
// Sample flat ground
bool sampling = true;
sampling = sampling && this->samplingLine(ground, line_vec[0], point_vec[0], friction_vec[0]);
sampling = sampling && this->samplingLine(ground, line_vec[1], point_vec[1], friction_vec[1]);
sampling = sampling && this->samplingLine(ground, line_vec[2], point_vec[2], friction_vec[2]);
sampling = sampling && this->samplingLine(ground, line_vec[3], point_vec[3], friction_vec[3]);
// Compute output point and normal
out.point = (point_vec[0] + point_vec[1] + point_vec[2] + point_vec[3]) / real(4.0);
out.normal = ((point_vec[0] - point_vec[1]).cross(point_vec[2] - point_vec[3])).normalized();
// Compute contact pose
vec3 e_y = rotation.col(1).normalized();
vec3 e_x = (out.normal.cross(e_y)).normalized();
vec3 e_z = (e_y.cross(e_x)).normalized();
// Compute contact depth
out.depth = radius * std::abs(out.normal.dot(e_z)) - (out.point - center_grd).norm();
// Compute remaining contact parameters
if (sampling && out.depth > real(0.0))
{
out.friction = (friction_vec[0] + friction_vec[1] + friction_vec[2] + friction_vec[3]) / real(4.0);
out.area = real(2.0) * std::sqrt(out.depth * (real(2.0) * radius - out.depth)) * width;
out.volume = (radius * radius * std::acos((radius - out.depth) / radius) -
(radius - out.depth) * std::sqrt(out.depth * (real(2.0) * radius - out.depth))) * width;
return true;
}
else
{
// No contact with the ground
this->envelop(pose, out);
return false;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool
rib::envelopSampling(
ground::flat const & ground,
affine const & pose,
output & out
)
const
{
// Rib alias variables
real const & radius = this->radius();
real const & width = this->m_width;
point const & center = this->center();
// Compute rib pose
point origin(pose.translation());
mat3 rotation(pose.linear());
vec3 normal_grd(rotation * this->normal());
point center_grd(origin + rotation * center);
// Store temporaries
real delta_x = real(0.1) * radius;
real delta_y = real(0.3) * width;
std::vector<line> line_vec(4);
std::vector<point> point_vec(4);
std::vector<real> friction_vec(4);
// Build sampling lines
line_vec[0] = line(origin + rotation * (center + delta_x * UNITX_VEC3), -UNITZ_VEC3);
line_vec[1] = line(origin + rotation * (center - delta_x * UNITX_VEC3), -UNITZ_VEC3);
line_vec[2] = line(origin + rotation * (center + delta_y * UNITY_VEC3), -UNITZ_VEC3);
line_vec[3] = line(origin + rotation * (center - delta_y * UNITY_VEC3), -UNITZ_VEC3);
// Sample flat ground
bool sampling = true;
sampling = sampling && this->samplingLine(ground, line_vec[0], point_vec[0], friction_vec[0]);
sampling = sampling && this->samplingLine(ground, line_vec[1], point_vec[1], friction_vec[1]);
sampling = sampling && this->samplingLine(ground, line_vec[2], point_vec[2], friction_vec[2]);
sampling = sampling && this->samplingLine(ground, line_vec[3], point_vec[3], friction_vec[3]);
// Compute output point and normal
out.point = (point_vec[0] + point_vec[1] + point_vec[2] + point_vec[3]) / real(4.0);
out.normal = ((point_vec[0] - point_vec[1]).cross(point_vec[2] - point_vec[3])).normalized();
// Compute contact pose
vec3 e_y = rotation.col(1).normalized();
vec3 e_x = (out.normal.cross(e_y)).normalized();
vec3 e_z = (e_y.cross(e_x)).normalized();
// Compute contact depth
out.depth = radius * std::abs(out.normal.dot(e_z)) - (out.point - center_grd).norm();
// Compute remaining contact parameters
if (sampling && out.depth > real(0.0))
{
out.friction = (friction_vec[0] + friction_vec[1] + friction_vec[2] + friction_vec[3]) / real(4.0);
out.area = real(2.0) * std::sqrt(out.depth * (real(2.0) * radius - out.depth)) * width;
out.volume = (radius * radius * std::acos((radius - out.depth) / radius) -
(radius - out.depth) * std::sqrt(out.depth * (real(2.0) * radius - out.depth))) * width;
return true;
}
else
{
// No contact with the ground
this->envelop(pose, out);
return false;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool
rib::samplingLine(
triangleground::vecptr const & ground,
line const & sampling_line,
point & contact_point,
real & contact_friction
)
const
{
#define CMD "enve::rib::samplingLine(...): "
size_t ground_size = ground.size();
point point_tmp;
std::vector<point> point_vec;
std::vector<real> friction_vec;
point_vec.reserve(ground_size);
friction_vec.reserve(ground_size);
bool int_bool = false;
for (size_t i = 0; i < ground_size; ++i)
{
if (Intersection(sampling_line, *ground[i], point_tmp, EPSILON_HIGH)) // FIXME! line to instatiated once
{
point_vec.push_back(point_tmp);
friction_vec.push_back(ground[i]->friction());
int_bool = true;
}
}
// Select the highest intersection point
if (point_vec.size() == 1 && int_bool)
{
contact_point = point_vec[0];
contact_friction = friction_vec[0];
return true;
}
else if (point_vec.size() > 1 && int_bool)
{
contact_point = point_vec[0];
contact_friction = friction_vec[0];
for (size_t j = 1; j < point_vec.size(); ++j)
{
if (point_vec[j].z() > contact_point.z())
{
contact_point = point_vec[j];
contact_friction = friction_vec[j];
}
}
return true;
}
else if (ground_size > 0 && !int_bool)
{
// Flying over the mesh
return false;
}
else if (ground_size == 0)
{
// Out of mesh
return false;
}
else
{
ENVE_ERROR(CMD "condition not handled.");
//return false;
}
#undef CMD
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool
rib::samplingLine(
ground::flat const & ground,
line const & sampling_line,
point & contact_point,
real & contact_friction
)
const
{
if (Intersection(sampling_line, ground, contact_point, EPSILON_HIGH))
{
contact_friction = ground.friction();
return true;
}
else
{
return false;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
} // namespace enve
#endif