///|
/// Triangle represented by three 3D vertices in continuous space.
pub struct Triangle3D {
  p0_x : Double
  p0_y : Double
  p0_z : Double
  p1_x : Double
  p1_y : Double
  p1_z : Double
  p2_x : Double
  p2_y : Double
  p2_z : Double
} derive(Eq, Debug, Default)

///|
/// Converts to internal geometry primitive.
fn Triangle3D::to_geometry(self : Triangle3D) -> @geometry.Triangle3D {
  @geometry.Triangle3D::new(
    self.p0_x,
    self.p0_y,
    self.p0_z,
    self.p1_x,
    self.p1_y,
    self.p1_z,
    self.p2_x,
    self.p2_y,
    self.p2_z,
  )
}

///|
/// Constructs a new Triangle3D with vertices (p0, p1, p2).
pub fn Triangle3D::new(
  p0_x : Double,
  p0_y : Double,
  p0_z : Double,
  p1_x : Double,
  p1_y : Double,
  p1_z : Double,
  p2_x : Double,
  p2_y : Double,
  p2_z : Double,
) -> Triangle3D {
  Triangle3D::{ p0_x, p0_y, p0_z, p1_x, p1_y, p1_z, p2_x, p2_y, p2_z, }
}

///|
/// Computes the tight Axis-Aligned Bounding Box (Box3D) enclosing the triangle.
pub fn Triangle3D::to_box(self : Triangle3D) -> Box3D {
  let b = self.to_geometry().to_box()
  Box3D::new(b.min_x, b.min_y, b.min_z, b.max_x, b.max_y, b.max_z)
}

///|
/// Computes the centroid (arithmetic center) of the triangle.
pub fn Triangle3D::centroid(self : Triangle3D) -> (Double, Double, Double) {
  self.to_geometry().centroid()
}

///|
/// Computes the surface normal vector of the triangle.
pub fn Triangle3D::normal(self : Triangle3D) -> (Double, Double, Double) {
  self.to_geometry().normal()
}

///|
/// Computes the surface area of the triangle.
pub fn Triangle3D::area(self : Triangle3D) -> Double {
  self.to_geometry().area()
}

///|
/// Tests if a 3D ray intersects the triangle, returning hit distance t.
pub fn Triangle3D::ray_intersection(
  self : Triangle3D,
  ox : Double,
  oy : Double,
  oz : Double,
  dx : Double,
  dy : Double,
  dz : Double,
) -> Double? {
  self.to_geometry().ray_intersection(ox, oy, oz, dx, dy, dz)
}

///|
/// Tests if a finite 3D line segment intersects the triangle.
pub fn Triangle3D::intersects_segment(
  self : Triangle3D,
  x1 : Double,
  y1 : Double,
  z1 : Double,
  x2 : Double,
  y2 : Double,
  z2 : Double,
) -> Bool {
  self.to_geometry().intersects_segment(x1, y1, z1, x2, y2, z2)
}

///|
/// Packed 3D spatial index for polygonal triangle meshes with accelerated ray-tracing and segment intersection.
pub struct StrMeshTree3D {
  priv inner : @tree.StrMeshTree3D
} derive(Eq, Debug, Default)

///|
/// Builds a packed 3D mesh index from an array of Triangle3D primitives.
pub fn StrMeshTree3D::build(
  triangles : Array[Triangle3D],
  node_capacity? : Int = 16,
) -> StrMeshTree3D {
  let g_triangles : Array[@geometry.Triangle3D] = []
  for i = 0; i < triangles.length(); i = i + 1 {
    g_triangles.push(triangles[i].to_geometry())
  }
  let inner = @tree.StrMeshTree3D::build(g_triangles, node_capacity~)
  StrMeshTree3D::{ inner, }
}

///|
/// Returns total number of indexed mesh triangles.
pub fn StrMeshTree3D::num_triangles(self : StrMeshTree3D) -> Int {
  self.inner.num_triangles()
}

///|
/// Retrieves triangle primitive by index.
pub fn StrMeshTree3D::get_triangle(
  self : StrMeshTree3D,
  id : Int,
) -> Triangle3D {
  let t = self.inner.get_triangle(id)
  Triangle3D::new(
    t.p0_x,
    t.p0_y,
    t.p0_z,
    t.p1_x,
    t.p1_y,
    t.p1_z,
    t.p2_x,
    t.p2_y,
    t.p2_z,
  )
}

///|
/// Queries triangle candidates intersecting a 3D bounding box.
pub fn StrMeshTree3D::query_box(
  self : StrMeshTree3D,
  search_box : Box3D,
) -> Array[Int] {
  self.inner.query_box(search_box)
}

///|
/// Queries mesh triangles that precisely intersect a 3D line segment.
pub fn StrMeshTree3D::query_segment(
  self : StrMeshTree3D,
  x1 : Double,
  y1 : Double,
  z1 : Double,
  x2 : Double,
  y2 : Double,
  z2 : Double,
) -> Array[Int] {
  self.inner.query_segment(x1, y1, z1, x2, y2, z2)
}

///|
/// Traces a ray and returns the closest intersecting triangle ID and distance t, or None if missed.
pub fn StrMeshTree3D::query_first_ray_hit(
  self : StrMeshTree3D,
  ox : Double,
  oy : Double,
  oz : Double,
  dx : Double,
  dy : Double,
  dz : Double,
  max_distance? : Double = 1000000.0,
) -> (Int, Double)? {
  self.inner.query_first_ray_hit(ox, oy, oz, dx, dy, dz, max_distance~)
}

///|
/// Traces a ray and returns all intersecting triangle IDs and hit distances t, sorted by distance ascending.
pub fn StrMeshTree3D::query_all_ray_hits(
  self : StrMeshTree3D,
  ox : Double,
  oy : Double,
  oz : Double,
  dx : Double,
  dy : Double,
  dz : Double,
  max_distance? : Double = 1000000.0,
) -> Array[(Int, Double)] {
  self.inner.query_all_ray_hits(ox, oy, oz, dx, dy, dz, max_distance~)
}