linear-sim 0.7.0

Minimal linear 3D simulation library
Documentation
extern crate test;

use rand;
use rand_distr;
use rand_xorshift::XorShiftRng;

use math_utils::geometry::*;

use super::*;

// 2ns
#[bench]
fn bench_sqrt (b : &mut test::Bencher) {
  let x = 10.0f64;
  b.iter (||{
    let _y = test::black_box (f64::sqrt (test::black_box (x)));
  });
}

// 4ns
#[bench]
fn bench_normalize (b : &mut test::Bencher) {
  let v = vector3 (10.0, 11.0, 12.0);
  b.iter (||{
    let _u = test::black_box (Unit3::normalize (test::black_box (v)));
  });
}

// 4ns
#[bench]
fn bench_rand_uniform (b : &mut test::Bencher) {
  use rand::{RngExt, SeedableRng};
  let mut rng = XorShiftRng::seed_from_u64 (0);
  let rand = |rng : &mut XorShiftRng| rng.random_range (-10.0..10.0);
  let mut sum = 0.0f64;
  b.iter (||{
    let x = rand (&mut rng);
    sum += x;
  });
  println!("SUM: {sum}");
}

// 10ns
#[bench]
fn bench_rand_cauchy (b : &mut test::Bencher) {
  use rand::SeedableRng;
  use rand_distr::Distribution;
  let mut rng = XorShiftRng::seed_from_u64 (0);
  let std_normal = rand_distr::StandardNormal;
  // cauchy sample
  let rand = |rng : &mut XorShiftRng| {
    let x : f64 = std_normal.sample (rng);
    let y : f64 = std_normal.sample (rng);
    x / y
  };
  let mut sum = 0.0f64;
  b.iter (||{
    let x = rand (&mut rng);
    sum += x;
  });
  println!("SUM: {sum}");
}

fn distance_sq_segment_point (segment : &Segment3 <f64>, point : &Point3 <f64>) ->
  (f64, Point3 <f64> , Point3 <f64>)
{
  let (_, nearest_a) = segment.nearest_point (*point);
  let d_sq = (point - nearest_a).magnitude_squared();
  (d_sq, nearest_a, *point)
}

// 9ns
#[bench]
fn bench_distance_sq_segment_point (b : &mut test::Bencher) {
  let segment = Segment3::noisy (
    [-2.0, -2.0, -2.0].into(),
    [ 2.0,  2.0,  2.0].into());
  let point = point3 (-1.0, 0.5, 0.0);
  b.iter (||{
    let (_dsq, _a, _b) = test::black_box (distance_sq_segment_point (
      test::black_box (&segment),
      test::black_box (&point)));
  });
}

// 17ns
#[bench]
fn bench_segment_point (b : &mut test::Bencher) {
  let segment = Segment3::noisy (
    [-2.0, -2.0, -2.0].into(),
    [ 2.0,  2.0,  2.0].into());
  let point = point3 (-1.0, 0.5, 0.0);
  b.iter (||{
    let _proximity = Proximity::query_segment_point (&segment, &point);
  });
}

// 49ns
#[bench]
fn bench_segment_segment (b : &mut test::Bencher) {
  let segment_a = Segment3::noisy (
    [-2.0, -2.0, -2.0].into(),
    [ 2.0,  2.0,  2.0].into());
  let segment_b = Segment3::noisy (
    [-2.0, 0.0, 0.0].into(),
    [ 2.0, 0.0, 0.0].into());
  b.iter (||{
    let _proximity = Proximity::query_segment_segment (&segment_a, &segment_b);
  });
}

// 3900ns
#[bench]
fn bench_triangle_triangle (b : &mut test::Bencher) {
  let mut triangle_a = Triangle3::noisy (
    [-2.0, -2.0, 0.0].into(),
    [ 2.0, -2.0, 0.0].into(),
    [ 0.0,  2.0, 0.0].into());
  let mut triangle_b = Triangle3::noisy (
    [ 0.0, 0.0, -2.0].into(),
    [-1.0, 0.0,  2.0].into(),
    [ 1.0, 0.0,  2.0].into());
  let proximity = Proximity::query_triangle_triangle (&triangle_a, &triangle_b);
  // make the arrangement asymmetrical so that the result is consistent
  triangle_a.translate (proximity.half_axis * 0.5);
  triangle_b.translate (-proximity.half_axis * 0.5);
  b.iter (||{
    let _proximity = Proximity::query_triangle_triangle (&triangle_a, &triangle_b);
  });
}

// 180ns
#[bench]
fn bench_distance_query_cuboid_edges_normalize (b : &mut test::Bencher) {
  use rand::SeedableRng;
  let mut rng = XorShiftRng::seed_from_u64 (0);
  b.iter (||{
    let (position_a, cuboid_a, position_b, cuboid_b) =
      distance_query_cuboid_edge_case (&mut rng);
    let proximity =
      Proximity::query_cuboid_cuboid (&position_a, &cuboid_a, &position_b, &cuboid_b);
    assert!(proximity.distance > 0.0);
  });
}

/*
// 180ns
#[bench]
fn bench_distance_query_cuboid_edges_array (b : &mut test::Bencher) {
  let mut rng = rs_utils::numeric::xorshift_rng_unseeded();
  b.iter (||{
    let (position_a, cuboid_a, position_b, cuboid_b) =
      distance_query_cuboid_edge_case (&mut rng);
    let proximity = Proximity::query_cuboid_cuboid_array (
      &position_a, &cuboid_a, &position_b, &cuboid_b);
    assert!(proximity.distance > 0.0);
  });
}

// 190ns
#[bench]
fn bench_distance_query_cuboid_edges_refactor (b : &mut test::Bencher) {
  let mut rng = rs_utils::numeric::xorshift_rng_unseeded();
  b.iter (||{
    let (position_a, cuboid_a, position_b, cuboid_b) =
      distance_query_cuboid_edge_case (&mut rng);
    let proximity = Proximity::query_cuboid_cuboid_refactor (
      &position_a, &cuboid_a, &position_b, &cuboid_b);
    assert!(proximity.distance > 0.0);
  });
}
*/

/// Generate an edge case for cuboid/cuboid distance benchmarks
fn distance_query_cuboid_edge_case <RNG : rand::RngExt> (rng : &mut RNG) -> (
  Point3 <f64>, shape::Cuboid <f64>,
  Point3 <f64>, shape::Cuboid <f64>
) {
  // overlapping range
  let base_min_a : f64 = rng.random_range (-10.0..10.0);
  let base_max_a : f64 = rng.random_range (base_min_a + 0.01..10.01);
  let base_min_b : f64 = rng.random_range (-10.01..base_max_a);
  let base_max_b : f64 = if base_min_b < base_min_a {
    rng.random_range (base_min_a + 0.01..10.02)
  } else {
    rng.random_range (base_min_b + 0.01..10.02)
  };
  debug_assert!(base_min_a < base_max_b);
  debug_assert!(base_min_b < base_max_a);
  let base_pos_a    = 0.5 * (base_min_a + base_max_a);
  let base_extent_a = base_max_a - base_pos_a;
  let base_pos_b    = 0.5 * (base_min_b + base_max_b);
  let base_extent_b = base_max_b - base_pos_b;
  debug_assert!(base_extent_a > 0.0);
  debug_assert!(base_extent_b > 0.0);
  // non-overlapping ranges
  let other1_min_a = rng.random_range (-10.0..10.0);
  let other1_max_a = rng.random_range (other1_min_a + 0.01..10.01);
  let (other1_min_b, other1_max_b) = if rng.random_bool (0.5) {
    // greater than
    let other1_min_b = rng.random_range (other1_max_a + 0.01..10.02);
    let other1_max_b = rng.random_range (other1_min_b + 0.01..10.03);
    debug_assert!(other1_max_a < other1_min_b);
    (other1_min_b, other1_max_b)
  } else {
    // less than
    let other1_min_b = rng.random_range (-10.03..other1_min_a - 0.03);
    let other1_max_b = rng.random_range (other1_min_b + 0.01..other1_min_a - 0.01);
    debug_assert!(other1_max_b < other1_min_a);
    (other1_min_b, other1_max_b)
  };
  let other1_pos_a    = 0.5 * (other1_min_a + other1_max_a);
  let other1_extent_a = other1_max_a - other1_pos_a;
  let other1_pos_b    = 0.5 * (other1_min_b + other1_max_b);
  let other1_extent_b = other1_max_b - other1_pos_b;
  debug_assert!(other1_extent_a > 0.0);
  debug_assert!(other1_extent_b > 0.0);

  let other2_min_a = rng.random_range (-10.0..10.0);
  let other2_max_a = rng.random_range (other2_min_a + 0.01..10.01);
  let (other2_min_b, other2_max_b) = if rng.random_bool (0.5) {
    // greater than
    let other2_min_b = rng.random_range (other2_max_a + 0.01..10.02);
    let other2_max_b = rng.random_range (other2_min_b + 0.01..10.03);
    debug_assert!(other2_max_a < other2_min_b);
    (other2_min_b, other2_max_b)
  } else {
    // less than
    let other2_min_b = rng.random_range (-10.03..other2_min_a - 0.03);
    let other2_max_b = rng.random_range (other2_min_b + 0.01..other2_min_a - 0.01);
    debug_assert!(other2_max_b < other2_min_a);
    (other2_min_b, other2_max_b)
  };
  let other2_pos_a    = 0.5 * (other2_min_a + other2_max_a);
  let other2_extent_a = other2_max_a - other2_pos_a;
  let other2_pos_b    = 0.5 * (other2_min_b + other2_max_b);
  let other2_extent_b = other2_max_b - other2_pos_b;
  debug_assert!(other2_extent_a > 0.0);
  debug_assert!(other2_extent_b > 0.0);

  let axis : usize = rng.random_range (0..3);

  if axis == 0 {
    // x
    (
      [base_pos_a, other1_pos_a, other2_pos_a].into(),
      shape::Cuboid::noisy ([base_extent_a, other1_extent_a, other2_extent_a]),
      [base_pos_b, other1_pos_b, other2_pos_b].into(),
      shape::Cuboid::noisy ([base_extent_b, other1_extent_b, other2_extent_b])
    )
  } else if axis == 1 {
    // y
    (
      [other1_pos_a, base_pos_a, other2_pos_a].into(),
      shape::Cuboid::noisy ([other1_extent_a, base_extent_a, other2_extent_a]),
      [other1_pos_b, base_pos_b, other2_pos_b].into(),
      shape::Cuboid::noisy ([other1_extent_b, base_extent_b, other2_extent_b])
    )
  } else {
    // z
    debug_assert_eq!(axis, 2);
    (
      [other1_pos_a, other2_pos_a, base_pos_a].into(),
      shape::Cuboid::noisy ([other1_extent_a, other2_extent_a, base_extent_a]),
      [other1_pos_b, other2_pos_b, base_pos_b].into(),
      shape::Cuboid::noisy ([other1_extent_b, other2_extent_b, base_extent_b])
    )
  }
}