44 #include <gtest/gtest.h> 
   83 template <
typename S> 
void NearestPointInCylinder() {
 
   98   for (S z_sign : {-1, 1}) {
 
   99       for (
const auto& dir : {Vector3<S>(1, 0, 0),
 
  101                               Vector3<S>(1, 1, 0).normalized(),
 
  102                               Vector3<S>(-1, 2, 0).normalized(),
 
  103                               Vector3<S>(1, -2, 0).normalized(),
 
  104                               Vector3<S>(-2, -3, 0).normalized()}) {
 
  105         const Vector3<S> z_offset_internal{0, 0, h * S(0.5) * z_sign};
 
  106         const Vector3<S> z_offset_external{0, 0, h * S(1.5) * z_sign};
 
  107         const Vector3<S> radial_offset_internal = dir * (
r * S(0.5));
 
  108         const Vector3<S> radial_offset_external = dir * (
r * S(1.5));
 
  110         using std::to_string;
 
  112         std::stringstream ss;
 
  113         ss << 
"dir: " << dir.transpose() << 
", z: " << z_sign;
 
  114         const std::string parameters = ss.str();
 
  116         p_CQ = radial_offset_internal + z_offset_internal;
 
  118         EXPECT_FALSE(N_is_not_S) << 
"Sphere at origin - " << parameters;
 
  121                   << 
"Sphere at origin - " << parameters;
 
  124         p_CQ = radial_offset_external + z_offset_internal;
 
  127                   << 
"Clamped by barrel - " << parameters;
 
  128         const Vector3<S> point_on_barrel = z_offset_internal + dir * 
r;
 
  129         EXPECT_NEAR(point_on_barrel(0), p_CN(0), Eps<S>::value())
 
  130                   << 
"Clamped by barrel - " << parameters;
 
  131         EXPECT_NEAR(point_on_barrel(1), p_CN(1), Eps<S>::value())
 
  132                   << 
"Clamped by barrel - " << parameters;
 
  134                   << 
"Clamped by barrel - " << parameters;
 
  137         p_CQ = radial_offset_internal + z_offset_external;
 
  139         EXPECT_TRUE(N_is_not_S) << 
"Clamped by end face - " << parameters;
 
  140         EXPECT_EQ(p_CQ(0), p_CN(0)) << 
"Clamped by end face - " << parameters;
 
  141         EXPECT_EQ(p_CQ(1), p_CN(1)) << 
"Clamped by end face - " << parameters;
 
  142         EXPECT_EQ(0.5 * h * z_sign, p_CN(2)) << 
"Clamped by end face - " 
  146         p_CQ = radial_offset_external + z_offset_external;
 
  148         EXPECT_TRUE(N_is_not_S) << 
"Fully clamped - " << parameters;
 
  149         EXPECT_NEAR(point_on_barrel(0), p_CN(0), Eps<S>::value())
 
  150                   << 
"Fully clamped - " << parameters;
 
  151         EXPECT_NEAR(point_on_barrel(1), p_CN(1), Eps<S>::value())
 
  152                   << 
"Fully clamped - " << parameters;
 
  153         EXPECT_EQ(0.5 * h * z_sign, p_CN(2)) << 
"Fully clamped - " 
  168 template <
typename S>
 
  169 struct TestConfiguration {
 
  170   TestConfiguration(
const std::string& name_in, 
const S& r_c_in,
 
  171                     const S& h_c_in, 
const S& r_s_in,
 
  172                     const Vector3<S> &p_CSo_in, 
bool colliding)
 
  209 template <
typename S>
 
  210 std::vector<TestConfiguration<S>> AppendLabel(
 
  211     const std::vector<TestConfiguration<S>>& configurations,
 
  212     const std::string& 
label) {
 
  213   std::vector<TestConfiguration<S>> configs;
 
  214   for (
const auto& config : configurations) {
 
  215     configs.push_back(config);
 
  216     configs.back().name += 
" - " + 
label;
 
  223 template <
typename S>
 
  224 std::vector<TestConfiguration<S>> GetUniformConfigurations() {
 
  225   std::vector<TestConfiguration<S>> configurations;
 
  230   const S half_h_c = h_c / 2;
 
  233   const bool collides = 
true;
 
  238   std::vector<Vector3<S>> barrel_directions{
 
  241       Vector3<S>(1, S(0.5), 0).normalized(),
 
  242       Vector3<S>(-1, S(0.5), 0).normalized(),
 
  243       Vector3<S>(-1, -S(0.5), 0).normalized(),
 
  244       Vector3<S>(1, -S(0.5), 0).normalized()};
 
  248     const Vector3<S> p_CS{
r_c * S(0.25), 
r_c * S(0.25),
 
  249                           half_h_c + 
r_s * S(1.1)};
 
  250     configurations.emplace_back(
 
  251         "Separated; nearest face +z", 
r_c, h_c, 
r_s, p_CS, !collides);
 
  253     TestConfiguration<S>& config = configurations.back();
 
  255     config.expected_distance = p_CS(2) - half_h_c - 
r_s;
 
  256     config.expected_p_CCs = Vector3<S>{p_CS(0), p_CS(1), half_h_c};
 
  257     config.expected_p_CSc = Vector3<S>{p_CS(0), p_CS(1), p_CS(2) - 
r_s};
 
  262     const S target_distance = 0.1;
 
  263     const Vector3<S> n_SC = Vector3<S>{-1, -1, 0}.normalized();
 
  264     const Vector3<S> p_CS = Vector3<S>{0, 0, half_h_c * S(0.5)} -
 
  265         n_SC * (
r_s + 
r_c + target_distance);
 
  266     configurations.emplace_back(
 
  267         "Separated; near barrel", 
r_c, h_c, 
r_s, p_CS, !collides);
 
  269     TestConfiguration<S>& config = configurations.back();
 
  271     config.expected_distance = target_distance;
 
  272     config.expected_p_CCs = -n_SC * 
r_c + Vector3<S>{0, 0, p_CS(2)};
 
  273     config.expected_p_CSc = p_CS + n_SC * 
r_s;
 
  278     const S target_distance =  .1;
 
  279     const Vector3<S> n_SC = Vector3<S>{-1, -1, -1}.normalized();
 
  280     const Vector3<S> p_CCs = Vector3<S>{0, 0, half_h_c} +
 
  281         Vector3<S>{-n_SC(0), -n_SC(1), 0}.normalized() * 
r_c;
 
  282     const Vector3<S> p_CS = p_CCs - n_SC * (
r_s + target_distance);
 
  283     configurations.emplace_back(
 
  284         "Separated; near barrel edge", 
r_c, h_c, 
r_s, p_CS, !collides);
 
  286     TestConfiguration<S>& config = configurations.back();
 
  288     config.expected_distance = target_distance;
 
  289     config.expected_p_CCs = p_CCs;
 
  290     config.expected_p_CSc = p_CS + n_SC * 
r_s;
 
  294   const S target_depth = 
min(
r_c, 
r_s) * S(0.25);
 
  298   for (S sign : {S(-1), S(1)}) {
 
  299     const Vector3<S> n_SC = Vector3<S>::UnitZ() * -sign;
 
  300     const Vector3<S> p_CCs = Vector3<S>{
r_c * S(0.25), 
r_c * S(0.25),
 
  302     const Vector3<S> p_CS = p_CCs - n_SC * (
r_s - target_depth);
 
  303     configurations.emplace_back(
 
  304         "Colliding external sphere center; cap face in " +
 
  305             (sign < 0 ? std::string(
"-z") : std::string(
"+z")),
 
  306         r_c, h_c, 
r_s, p_CS, collides);
 
  308     TestConfiguration<S>& config = configurations.back();
 
  309     config.expected_depth = target_depth;
 
  310     config.expected_normal = n_SC;
 
  311     config.expected_pos = p_CCs + n_SC * (target_depth / 2);
 
  317   for (
const Vector3<S>& n_CS : barrel_directions) {
 
  318     const Vector3<S> p_CCs = Vector3<S>{0, 0, half_h_c * S(.1)} +
 
  320     const Vector3<S> p_CS = p_CCs + n_CS * (
r_s - target_depth);
 
  321     std::stringstream ss;
 
  322     ss << 
"Colliding external sphere center; barrel from sphere center in" 
  323        << n_CS.transpose() << 
" direction";
 
  324     configurations.emplace_back(ss.str(), 
r_c, h_c, 
r_s, p_CS, collides);
 
  326     TestConfiguration<S>& config = configurations.back();
 
  327     config.expected_depth = target_depth;
 
  328     config.expected_normal = -n_CS;
 
  329     config.expected_pos = p_CCs - n_CS * (target_depth / 2);
 
  334   for (S sign : {S(-1), S(1)}) {
 
  337     for (
const Vector3<S>& n_CS_xy : barrel_directions) {
 
  338       const Vector3<S> p_CCs = Vector3<S>{0, 0, sign * half_h_c} +
 
  340       const Vector3<S> n_CS = p_CCs.normalized();
 
  341       const Vector3<S> p_CS = p_CCs + n_CS * (
r_s - target_depth);
 
  342       std::stringstream ss;
 
  343       ss << 
"Colliding external sphere center; edge from sphere center in" 
  344          << n_CS.transpose() << 
" direction";
 
  345       configurations.emplace_back(ss.str(), 
r_c, h_c, 
r_s, p_CS, collides);
 
  347       TestConfiguration<S>& config = configurations.back();
 
  348       config.expected_depth = target_depth;
 
  349       config.expected_normal = -n_CS;
 
  350       config.expected_pos = p_CCs - n_CS * (target_depth / 2);
 
  359   for (S sign : {S(-1), S(1)}) {
 
  362     const Vector3<S> n_SC = Vector3<S>::UnitZ() * -sign;
 
  363     const Vector3<S> p_CCs = Vector3<S>{
r_c * S(0.25), 
r_c * S(0.25),
 
  365     const Vector3<S> p_CS = p_CCs + n_SC * d_SF;
 
  366     configurations.emplace_back(
 
  367         "Colliding internal sphere center; cap face in " +
 
  368             (sign < 0 ? std::string(
"-z") : std::string(
"+z")),
 
  369         r_c, h_c, 
r_s, p_CS, collides);
 
  371     TestConfiguration<S>& config = configurations.back();
 
  372     config.expected_depth = d_SF + 
r_s;
 
  373     config.expected_normal = n_SC;
 
  374     config.expected_pos = p_CCs + n_SC * (config.expected_depth / 2);
 
  380   for (
const Vector3<S>& n_CS : barrel_directions) {
 
  383     const Vector3<S> p_CCs = Vector3<S>{0, 0, half_h_c * S(.1)} +
 
  385     const Vector3<S> p_CS = p_CCs - n_CS * d_SB;
 
  386     std::stringstream ss;
 
  387     ss << 
"Colliding internal sphere center; barrel from sphere located in " 
  388        << n_CS.transpose() << 
" direction";
 
  389     configurations.emplace_back(ss.str(), 
r_c, h_c, 
r_s, p_CS, collides);
 
  391     TestConfiguration<S>& config = configurations.back();
 
  392     config.expected_depth = 
r_s + d_SB;
 
  393     config.expected_normal = -n_CS;
 
  394     config.expected_pos = p_CCs - n_CS * (config.expected_depth / 2);
 
  404   for (S sign : {S(-1), S(1)}) {
 
  406     const S d_SF = Eps<S>::value() / 2;
 
  407     const Vector3<S> n_SC = Vector3<S>::UnitZ() * -sign;
 
  408     const Vector3<S> p_CCs = Vector3<S>{
r_c * S(0.25), 
r_c * S(0.25),
 
  410     const Vector3<S> p_CS = p_CCs - n_SC * d_SF;
 
  411     configurations.emplace_back(
 
  412         "Colliding sphere center outside by ε; cap face in " +
 
  413             (sign < 0 ? std::string(
"-z") : std::string(
"+z")),
 
  414         r_c, h_c, 
r_s, p_CS, collides);
 
  416     TestConfiguration<S>& config = configurations.back();
 
  417     config.expected_depth = 
r_s;
 
  418     config.expected_normal = n_SC;
 
  419     config.expected_pos = p_CCs + n_SC * (config.expected_depth / 2);
 
  427   for (
const Vector3<S>& n_CS : barrel_directions) {
 
  429     const S d_SB = Eps<S>::value() / 2;
 
  430     const Vector3<S> p_CCs = Vector3<S>{0, 0, half_h_c * S(.1)} +
 
  432     const Vector3<S> p_CS = p_CCs - n_CS * d_SB;
 
  433     std::stringstream ss;
 
  434     ss << 
"Colliding sphere center outside by ε; barrel from sphere located in " 
  435        << n_CS.transpose() << 
" direction";
 
  436     configurations.emplace_back(ss.str(), 
r_c, h_c, 
r_s, p_CS, collides);
 
  438     TestConfiguration<S>& config = configurations.back();
 
  439     config.expected_depth = 
r_s;
 
  440     config.expected_normal = -n_CS;
 
  441     config.expected_pos = p_CCs - n_CS * (config.expected_depth / 2);
 
  451   for (S sign : {S(-1), S(1)}) {
 
  454     const S d_SC = Eps<S>::value() / 2;
 
  455     for (
const Vector3<S>& n_CS_xy : barrel_directions) {
 
  456       const Vector3<S> p_CCs = Vector3<S>{0, 0, sign * half_h_c} +
 
  458       const Vector3<S> n_CS = p_CCs.normalized();
 
  459       const Vector3<S> p_CS = p_CCs + n_CS * d_SC;
 
  460       std::stringstream ss;
 
  461       ss << 
"Colliding sphere center outside by ε; edge from sphere center in" 
  462          << n_CS.transpose() << 
" direction";
 
  463       configurations.emplace_back(ss.str(), 
r_c, h_c, 
r_s, p_CS, collides);
 
  465       TestConfiguration<S>& config = configurations.back();
 
  466       config.expected_depth = 
r_s;
 
  469       config.expected_normal = -sign * Vector3<S>::UnitZ();
 
  470       config.expected_pos = p_CCs + config.expected_normal * (
r_s / 2);
 
  478     const Vector3<S> p_CS = Vector3<S>::Zero();
 
  480     const S big_radius = h_c * 2;
 
  481     configurations.emplace_back(
 
  482         "Collision with sphere at origin; face nearest", big_radius, h_c, 
r_s,
 
  485     TestConfiguration<S>& config = configurations.back();
 
  486     config.expected_depth = 
r_s + h_c / 2;
 
  487     config.expected_normal = -Vector3<S>::UnitZ();
 
  488     config.expected_pos = Vector3<S>{0, 0, h_c / 2 - config.expected_depth / 2};
 
  494     const Vector3<S> p_CS = Vector3<S>::Zero();
 
  496     const S big_height = 
r_c * 4;
 
  497     configurations.emplace_back(
 
  498         "Collision with sphere at origin; barrel nearest", 
r_c, big_height, 
r_s,
 
  501     TestConfiguration<S>& config = configurations.back();
 
  502     config.expected_depth = 
r_s + 
r_c;
 
  503     config.expected_normal = -Vector3<S>::UnitX();
 
  504     config.expected_pos = Vector3<S>{
r_c - config.expected_depth / 2, 0, 0};
 
  508   return configurations;
 
  513 template <
typename S>
 
  514 std::vector<TestConfiguration<S>> GetNonUniformConfigurations() {
 
  515   std::vector<TestConfiguration<S>> configurations;
 
  522     const bool collides = 
true;
 
  523     const S target_depth = 
r_s / 2;
 
  528       const Vector3<S> p_CCs = Vector3<S>(1, 2, 0).normalized() * (
r_c - 
r_s) +
 
  529           Vector3<S>::UnitZ() * (h_c / 2);
 
  530       const Vector3<S> p_CS{p_CCs + Vector3<S>::UnitZ() * (
r_s - target_depth)};
 
  531       configurations.emplace_back(
 
  532           "Collision large disk, small sphere - contact at face",
 
  533           r_c, h_c, 
r_s, p_CS, collides);
 
  535       TestConfiguration<S>& config = configurations.back();
 
  536       config.expected_depth = target_depth;
 
  537       config.expected_normal = -Vector3<S>::UnitZ();
 
  538       config.expected_pos = p_CCs - Vector3<S>::UnitZ() * (target_depth / 2);
 
  545       const Vector3<S> p_CCs = Vector3<S>(1, 2, 0).normalized() * (
r_c - 
r_s) +
 
  546           Vector3<S>::UnitZ() * (h_c / 2);
 
  547       const Vector3<S> p_CS{p_CCs +
 
  548           Vector3<S>::UnitZ() * (
r_s + target_depth)};
 
  549       configurations.emplace_back(
 
  550           "Separation large disk, small sphere - nearest +z face",
 
  551           r_c, h_c, 
r_s, p_CS, !collides);
 
  553       TestConfiguration<S>& config = configurations.back();
 
  555       config.expected_distance = target_depth;
 
  556       config.expected_p_CCs = p_CCs;
 
  557       config.expected_p_CSc = p_CS - Vector3<S>::UnitZ() * 
r_s;
 
  561     const Vector3<S> n_CS = Vector3<S>(1, 2, 0).normalized();
 
  562     const Vector3<S> p_CCs = n_CS * 
r_c + Vector3<S>::UnitZ() * (
r_s * 0.1);
 
  565       const Vector3<S> p_CS{p_CCs + n_CS * (
r_s - target_depth)};
 
  566       configurations.emplace_back(
 
  567           "Collision large disk, small sphere - contact at barrel",
 
  568           r_c, h_c, 
r_s, p_CS, collides);
 
  570       TestConfiguration<S>& config = configurations.back();
 
  571       config.expected_depth = target_depth;
 
  572       config.expected_normal = -n_CS;
 
  573       config.expected_pos = p_CCs - n_CS * (target_depth / 2);
 
  580       const Vector3<S> p_CS{p_CCs + n_CS * (
r_s + target_depth)};
 
  581       configurations.emplace_back(
 
  582           "Separation large disk, small sphere - nearest barrel",
 
  583           r_c, h_c, 
r_s, p_CS, !collides);
 
  585       TestConfiguration<S>& config = configurations.back();
 
  587       config.expected_distance = target_depth;
 
  588       config.expected_p_CCs = p_CCs;
 
  589       config.expected_p_CSc = p_CS - n_CS * 
r_s;
 
  598     const bool collides = 
true;
 
  599     const S target_depth = 
r_c / 2;
 
  603       const Vector3<S> p_CCs =
 
  604           Vector3<S>(1, 2, 0).normalized() * (
r_c * S(0.5)) +
 
  605               Vector3<S>::UnitZ() * (h_c / 2);
 
  610             p_CS{p_CCs + Vector3<S>::UnitZ() * (
r_s - target_depth)};
 
  611         configurations.emplace_back(
 
  612             "Collision large sphere, small disk - contact at face",
 
  613             r_c, h_c, 
r_s, p_CS, collides);
 
  615         TestConfiguration<S>& config = configurations.back();
 
  616         config.expected_depth = target_depth;
 
  617         config.expected_normal = -Vector3<S>::UnitZ();
 
  618         config.expected_pos = p_CCs - Vector3<S>::UnitZ() * (target_depth / 2);
 
  624         const Vector3<S> p_CS{p_CCs +
 
  625             Vector3<S>::UnitZ() * (
r_s + target_depth)};
 
  626         configurations.emplace_back(
 
  627             "Separation large sphere, small disk - nearest +z face",
 
  628             r_c, h_c, 
r_s, p_CS, !collides);
 
  630         TestConfiguration<S>& config = configurations.back();
 
  632         config.expected_distance = target_depth;
 
  633         config.expected_p_CCs = p_CCs;
 
  634         config.expected_p_CSc = p_CS - Vector3<S>::UnitZ() * 
r_s;
 
  640       const Vector3<S> n_CS = Vector3<S>(1, 2, 0).normalized();
 
  641       const Vector3<S> p_CCs = n_CS * 
r_c + Vector3<S>::UnitZ() * (h_c * 0.1);
 
  645         const Vector3<S> p_CS{p_CCs + n_CS * (
r_s - target_depth)};
 
  646         configurations.emplace_back(
 
  647             "Collision large sphere, small disk - contact at barrel",
 
  648             r_c, h_c, 
r_s, p_CS, collides);
 
  650         TestConfiguration<S>& config = configurations.back();
 
  651         config.expected_depth = target_depth;
 
  652         config.expected_normal = -n_CS;
 
  653         config.expected_pos = p_CCs - n_CS * (target_depth / 2);
 
  659         const Vector3<S> p_CS{p_CCs + n_CS * (
r_s + target_depth)};
 
  660         configurations.emplace_back(
 
  661             "Separation large sphere, small disk - nearest barrel",
 
  662             r_c, h_c, 
r_s, p_CS, !collides);
 
  664         TestConfiguration<S>& config = configurations.back();
 
  666         config.expected_distance = target_depth;
 
  667         config.expected_p_CCs = p_CCs;
 
  668         config.expected_p_CSc = p_CS - n_CS * 
r_s;
 
  673   return configurations;
 
  676 template <
typename S>
 
  678 std::function<void(
const TestConfiguration<S>&, 
const Transform3<S>&,
 
  679                    const Matrix3<S>&, S)>;
 
  688 template <
typename S>
 
  689 void EvalCollisionForTestConfiguration(
const TestConfiguration<S>& config,
 
  690                                        const Transform3<S>& X_WC,
 
  691                                        const Matrix3<S>& R_CS,
 
  694   Cylinder<S> cylinder(config.r_c, config.cylinder_half_len * S(2));
 
  695   Sphere<S> sphere{config.r_s};
 
  696   Transform3<S> X_CS = Transform3<S>::Identity();
 
  697   X_CS.translation() = config.p_CSo;
 
  698   X_CS.linear() = R_CS;
 
  699   Transform3<S> X_WS = X_WC * X_CS;
 
  701   bool colliding = sphereCylinderIntersect<S>(sphere, X_WS, cylinder, X_WC,
 
  703   EXPECT_EQ(config.expected_colliding, colliding) << config.name;
 
  705   std::vector<ContactPoint<S>> contacts;
 
  706   colliding = sphereCylinderIntersect<S>(sphere, X_WS, cylinder, X_WC, &contacts);
 
  707   EXPECT_EQ(colliding, config.expected_colliding) << config.name;
 
  708   if (config.expected_colliding) {
 
  709     EXPECT_EQ(1u, contacts.size()) << config.name;
 
  710     const ContactPoint<S> &contact = contacts[0];
 
  711     EXPECT_NEAR(config.expected_depth, contact.penetration_depth, eps)
 
  714                                 X_WC.linear() * config.expected_normal, eps,
 
  721     EXPECT_EQ(contacts.size(), 0u) << config.name;
 
  732 template <
typename S>
 
  733 void EvalDistanceForTestConfiguration(
const TestConfiguration<S>& config,
 
  734                                       const Transform3<S>& X_WC,
 
  735                                       const Matrix3<S>& R_CS,
 
  738   Cylinder<S> cylinder(config.r_c, config.cylinder_half_len * S(2));
 
  739   Sphere<S> sphere{config.r_s};
 
  740   Transform3<S> X_CS = Transform3<S>::Identity();
 
  741   X_CS.translation() = config.p_CSo;
 
  742   X_CS.linear() = R_CS;
 
  743   Transform3<S> X_WS = X_WC * X_CS;
 
  745   bool separated = sphereCylinderDistance<S>(sphere, X_WS, cylinder, X_WC,
 
  746                                              nullptr, 
nullptr, 
nullptr);
 
  747   EXPECT_NE(separated, config.expected_colliding) << config.name;
 
  752   Vector3<S> p_WSc{0, 0, 0};
 
  753   Vector3<S> p_WCs{0, 0, 0};
 
  755   separated = sphereCylinderDistance<S>(sphere, X_WS, cylinder, X_WC, &
distance,
 
  757   EXPECT_NE(separated, config.expected_colliding) << config.name;
 
  758   if (!config.expected_colliding) {
 
  762                                 X_WC * config.expected_p_CSc, eps,
 
  766                                 X_WC * config.expected_p_CCs, eps,
 
  779 template <
typename S>
 
  780 void QueryWithVaryingWorldFrames(
 
  781     const std::vector<TestConfiguration<S>>& configurations,
 
  782     EvalFunc<S> query_eval, 
const Matrix3<S>& R_CS = Matrix3<S>::Identity()) {
 
  784   auto evaluate_all = [&R_CS, query_eval](
 
  785       const std::vector<TestConfiguration<S>>& configs,
 
  786       const Transform3<S>& X_FC) {
 
  787     for (
const auto config : configs) {
 
  788       query_eval(config, X_FC, R_CS, Eps<S>::value());
 
  793   Transform3<S> X_FC = Transform3<S>::Identity();
 
  794   evaluate_all(AppendLabel(configurations, 
"X_FC = I"), X_FC);
 
  797   X_FC.translation() << 1.3, 2.7, 6.5;
 
  798   evaluate_all(AppendLabel(configurations, 
"X_FC is translation"), X_FC);
 
  800   std::string axis_name[] = {
"x", 
"y", 
"z"};
 
  802   for (
int axis = 0; axis < 3; ++axis) {
 
  803     std::string 
label = 
"X_FC is 90-degree rotation around " + axis_name[axis];
 
  805     X_FC.linear() << angle_axis.matrix();
 
  806     evaluate_all(AppendLabel(configurations, 
label), X_FC);
 
  812                             Vector3<S>{1, 2, 3}.normalized()};
 
  813     X_FC.linear() << angle_axis.matrix();
 
  814     evaluate_all(AppendLabel(configurations, 
"X_FC is arbitrary rotation"),
 
  821     X_FC.linear() << angle_axis.matrix();
 
  822     evaluate_all(AppendLabel(configurations, 
"X_FC is near identity"),
 
  830 template <
typename S>
 
  831 void QueryWithOrientedSphere(
 
  832     const std::vector<TestConfiguration<S>>& configurations,
 
  833     EvalFunc<S> query_eval) {
 
  835   std::string axis_name[] = {
"x", 
"y", 
"z"};
 
  838   for (
int axis = 0; axis < 3; ++axis) {
 
  840     std::string 
label = 
"sphere rotate 90-degrees around " + axis_name[axis];
 
  841     QueryWithVaryingWorldFrames<S>(AppendLabel(configurations, 
label),
 
  842                                    query_eval, angle_axis.matrix());
 
  848                             Vector3<S>{1, 2, 3}.normalized()};
 
  849     std::string 
label = 
"sphere rotated arbitrarily";
 
  850     QueryWithVaryingWorldFrames<S>(AppendLabel(configurations, 
label),
 
  851                                    query_eval, angle_axis.matrix());
 
  857     std::string 
label = 
"sphere rotated near axes";
 
  858     QueryWithVaryingWorldFrames<S>(AppendLabel(configurations, 
label),
 
  859                                    query_eval, angle_axis.matrix());
 
  866 GTEST_TEST(SphereCylinderPrimitiveTest, NearestPointInCylinder) {
 
  867   NearestPointInCylinder<float>();
 
  868   NearestPointInCylinder<double>();
 
  873 GTEST_TEST(SphereCylinderPrimitiveTest, CollisionAcrossVaryingWorldFrames) {
 
  874   QueryWithVaryingWorldFrames<float>(GetUniformConfigurations<float>(),
 
  875                                      EvalCollisionForTestConfiguration<float>);
 
  876   QueryWithVaryingWorldFrames<double>(
 
  877       GetUniformConfigurations<double>(),
 
  878       EvalCollisionForTestConfiguration<double>);
 
  883 GTEST_TEST(SphereCylinderPrimitiveTest, CollisionWithSphereRotations) {
 
  884   QueryWithOrientedSphere<float>(GetUniformConfigurations<float>(),
 
  885                                  EvalCollisionForTestConfiguration<float>);
 
  886   QueryWithOrientedSphere<double>(GetUniformConfigurations<double>(),
 
  887                                   EvalCollisionForTestConfiguration<double>);
 
  893 GTEST_TEST(SphereCylinderPrimitiveTest, CollisionIncompatibleScales) {
 
  894   QueryWithVaryingWorldFrames<float>(GetNonUniformConfigurations<float>(),
 
  895                                      EvalCollisionForTestConfiguration<float>);
 
  896   QueryWithVaryingWorldFrames<double>(
 
  897       GetNonUniformConfigurations<double>(),
 
  898       EvalCollisionForTestConfiguration<double>);
 
  903 GTEST_TEST(SphereCylinderPrimitiveTest, DistanceAcrossVaryingWorldFrames) {
 
  904   QueryWithVaryingWorldFrames<float>(GetUniformConfigurations<float>(),
 
  905                                      EvalDistanceForTestConfiguration<float>);
 
  906   QueryWithVaryingWorldFrames<double>(GetUniformConfigurations<double>(),
 
  907                                       EvalDistanceForTestConfiguration<double>);
 
  912 GTEST_TEST(SphereCylinderPrimitiveTest, DistanceWithSphereRotations) {
 
  913   QueryWithOrientedSphere<float>(GetUniformConfigurations<float>(),
 
  914                                  EvalDistanceForTestConfiguration<float>);
 
  915   QueryWithOrientedSphere<double>(GetUniformConfigurations<double>(),
 
  916                                   EvalDistanceForTestConfiguration<double>);
 
  922 GTEST_TEST(SphereCylinderPrimitiveTest, DistanceIncompatibleScales) {
 
  923   QueryWithVaryingWorldFrames<float>(GetNonUniformConfigurations<float>(),
 
  924                                      EvalDistanceForTestConfiguration<float>);
 
  925   QueryWithVaryingWorldFrames<double>(GetNonUniformConfigurations<double>(),
 
  926                                       EvalDistanceForTestConfiguration<double>);
 
  934 int main(
int argc, 
char *argv[]) {
 
  935   ::testing::InitGoogleTest(&argc, argv);
 
  936   return RUN_ALL_TESTS();