///|
/// 有符号距离场(SDF)。
///
/// 约定:实体内部为负、外部为正、边界为 0。所有原语与布尔都保持这个约定,
/// 因为它们只由 `min` / `max` / `-` 组合,而这三个运算保号。
///
/// 树是公开的(`pub(all)`):调用方可以直接构造,也可以把自己的节点
/// 写进 `eval` 之外的分析(例如 `bounds`)。递归节点不做共享引用,
/// 同一棵子树被复用两次就是两份拷贝——几何树很小,不做引用计数。
pub(all) enum Sdf {
  /// 球:`center` + `radius`。
  Sphere(Vec3, Double)
  /// 盒:`center` + 逐轴半长 `half`。
  Box(Vec3, Vec3)
  /// 半空间:`dot(p, normal) = offset`,法线朝外(正侧是外部)。
  /// `normal` 在构造时归一化,直接字面量构造则按传入值计算。
  Plane(Vec3, Double)
  /// 圆柱:轴沿 z,`center` + `radius` + 半高 `half_height`。
  Cylinder(Vec3, Double, Double)
  /// 环面:轴沿 z,`center` + 母线半径 `big` + 管半径 `small`。
  Torus(Vec3, Double, Double)
  /// 平移:先减去 `t` 再求值。
  Translate(Sdf, Vec3)
  /// 旋转:`m` 的列是局部基,求值时用转置把世界坐标拉回局部。
  Rotate(Sdf, Mat3)
  /// 均匀缩放。非正值视为空实体(SDF 恒为 0,网格化会得到空结果)。
  Scale(Sdf, Double)
  /// 并:`min(a, b)`。
  Union(Sdf, Sdf)
  /// 交:`max(a, b)`。
  Intersect(Sdf, Sdf)
  /// 差:`max(a, -b)`,`b` 是刀具。
  Subtract(Sdf, Sdf)
  /// 平滑并,`k` 是过渡带宽度;`k <= 0` 退化为硬并。
  SmoothUnion(Sdf, Sdf, Double)
}

///|
pub fn Sdf::sphere(radius : Double) -> Sdf {
  Sphere(Vec3::zero(), radius)
}

///|
pub fn Sdf::box(half : Vec3) -> Sdf {
  Box(Vec3::zero(), half)
}

///|
/// 法线在这里归一化:`Plane` 的距离公式要求 `|normal| = 1`,
/// 否则返回值会被整体缩放。
pub fn Sdf::plane(normal : Vec3, offset : Double) -> Sdf {
  Plane(normal.normalize(), offset)
}

///|
pub fn Sdf::cylinder(radius : Double, half_height : Double) -> Sdf {
  Cylinder(Vec3::zero(), radius, half_height)
}

///|
pub fn Sdf::at(self : Sdf, center : Vec3) -> Sdf {
  Translate(self, center)
}

///|
pub fn Sdf::union(self : Sdf, o : Sdf) -> Sdf {
  Union(self, o)
}

///|
pub fn Sdf::intersect(self : Sdf, o : Sdf) -> Sdf {
  Intersect(self, o)
}

///|
pub fn Sdf::subtract(self : Sdf, o : Sdf) -> Sdf {
  Subtract(self, o)
}

///|
pub fn Sdf::smooth_union(self : Sdf, o : Sdf, k : Double) -> Sdf {
  SmoothUnion(self, o, k)
}

///|
pub fn Sdf::scale(self : Sdf, s : Double) -> Sdf {
  Scale(self, s)
}

///|
fn max3(v : Vec3) -> Double {
  let m = if v.x > v.y { v.x } else { v.y }
  if m > v.z {
    m
  } else {
    v.z
  }
}

///|
fn clamp01(v : Double) -> Double {
  if v < 0.0 {
    0.0
  } else if v > 1.0 {
    1.0
  } else {
    v
  }
}

///|
fn len2(a : Double, b : Double) -> Double {
  (a * a + b * b).sqrt()
}

///|
/// 求有符号距离。这是整个内核唯一的「真相来源」:网格化、体积、法线
/// 全部由它派生,所以每个原语都直接照抄解析公式,不做近似。
pub fn Sdf::eval(self : Sdf, p : Vec3) -> Double {
  match self {
    Sphere(c, r) => p.sub(c).length() - r
    Box(c, half) => {
      let q = p.sub(c).abs().sub(half)
      let inside = max3(q)
      q.max_zero().length() + (if inside < 0.0 { inside } else { 0.0 })
    }
    Plane(n, d) => p.dot(n) - d
    Cylinder(c, r, h) => {
      let q = p.sub(c)
      let radial = len2(q.x, q.y) - r
      let axial = (if q.z < 0.0 { -q.z } else { q.z }) - h
      if radial < 0.0 && axial < 0.0 {
        if radial > axial {
          radial
        } else {
          axial
        }
      } else {
        len2(
          if radial > 0.0 {
            radial
          } else {
            0.0
          },
          if axial > 0.0 {
            axial
          } else {
            0.0
          },
        )
      }
    }
    Torus(c, big, small) => {
      let q = p.sub(c)
      len2(len2(q.x, q.y) - big, q.z) - small
    }
    Translate(s, t) => s.eval(p.sub(t))
    Rotate(s, m) => s.eval(m.transpose().mul_vec(p))
    Scale(s, k) => if k <= 0.0 { 0.0 } else { s.eval(p.div(k)) * k }
    Union(a, b) => {
      let da = a.eval(p)
      let db = b.eval(p)
      if da < db {
        da
      } else {
        db
      }
    }
    Intersect(a, b) => {
      let da = a.eval(p)
      let db = b.eval(p)
      if da > db {
        da
      } else {
        db
      }
    }
    Subtract(a, b) => {
      let da = a.eval(p)
      let db = -b.eval(p)
      if da > db {
        da
      } else {
        db
      }
    }
    SmoothUnion(a, b, k) =>
      if k <= 0.0 {
        let da = a.eval(p)
        let db = b.eval(p)
        if da < db {
          da
        } else {
          db
        }
      } else {
        let da = a.eval(p)
        let db = b.eval(p)
        let h = clamp01(0.5 + 0.5 * (db - da) / k)
        db + (da - db) * h - k * h * (1.0 - h)
      }
  }
}

///|
/// 内部/外部判定。`f >= 0` 一律算外部:`f == 0` 的抖动会让网格化漏面或重面,
/// 所以边界点统一归到外部,由采样偏置处理(见 ARCHITECTURE)。
pub fn Sdf::is_inside(self : Sdf, p : Vec3) -> Bool {
  self.eval(p) < 0.0
}

///|
/// 数值梯度(中心差分)指向距离增大方向,也就是实体外部。
///
/// 步长随坐标尺度放大,避免在远处把差分吃掉;这里只用 `+ - * / sqrt`,
/// 不引入三角函数,保证同一输入的梯度在任何后端一致。
pub fn Sdf::gradient(self : Sdf, p : Vec3) -> Vec3 {
  let h = 1.0e-5 * (1.0 + p.length())
  let dx = self.eval(p.add(Vec3::new(h, 0.0, 0.0))) -
    self.eval(p.sub(Vec3::new(h, 0.0, 0.0)))
  let dy = self.eval(p.add(Vec3::new(0.0, h, 0.0))) -
    self.eval(p.sub(Vec3::new(0.0, h, 0.0)))
  let dz = self.eval(p.add(Vec3::new(0.0, 0.0, h))) -
    self.eval(p.sub(Vec3::new(0.0, 0.0, h)))
  Vec3::new(dx, dy, dz).div(2.0 * h)
}

///|
/// 保守包围盒:只做「不会漏掉实体」的放大,不求紧。
/// 半空间没有有限包围盒,参与 `Union` 时整体返回 `None`,
/// 参与 `Intersect` 时由另一侧决定,做刀具时被忽略。
pub fn Sdf::bounds(self : Sdf) -> Aabb? {
  match self {
    Sphere(c, r) => Some(Aabb::from_center_half(c, Vec3::splat(r)))
    Box(c, half) => Some(Aabb::from_center_half(c, half))
    Plane(_, _) => None
    Cylinder(c, r, h) => {
      let rr = if r < 0.0 { 0.0 } else { r }
      Some(Aabb::from_center_half(c, Vec3::new(rr, rr, h)))
    }
    Torus(c, big, small) =>
      Some(
        Aabb::from_center_half(
          c,
          Vec3::new(
            (if big > 0.0 { big } else { 0.0 }) +
            (if small > 0.0 { small } else { 0.0 }),
            (if big > 0.0 { big } else { 0.0 }) +
            (if small > 0.0 { small } else { 0.0 }),
            if small > 0.0 {
              small
            } else {
              0.0
            },
          ),
        ),
      )
    Translate(s, t) =>
      match s.bounds() {
        Some(b) => Some(Aabb::new(b.min.add(t), b.max.add(t)))
        None => None
      }
    Rotate(s, m) =>
      match s.bounds() {
        Some(b) => {
          // 旋转后的 AABB 取包围盒八个角点的包络。
          let mut out = Aabb::new(Vec3::splat(1.0e300), Vec3::splat(-1.0e300))
          let mut i = 0
          while i < 8 {
            let p = Vec3::new(
              if (i & 1) == 0 {
                b.min.x
              } else {
                b.max.x
              },
              if (i & 2) == 0 {
                b.min.y
              } else {
                b.max.y
              },
              if (i & 4) == 0 {
                b.min.z
              } else {
                b.max.z
              },
            )
            let q = m.mul_vec(p)
            out = Aabb::new(out.min.min(q), out.max.max(q))
            i = i + 1
          }
          Some(out)
        }
        None => None
      }
    Scale(s, k) =>
      match s.bounds() {
        Some(b) =>
          if k <= 0.0 {
            None
          } else {
            Some(Aabb::new(b.min.mul(k), b.max.mul(k)))
          }
        None => None
      }
    Union(a, b) =>
      match (a.bounds(), b.bounds()) {
        (Some(x), Some(y)) => Some(x.union(y))
        _ => None
      }
    SmoothUnion(a, b, k) =>
      match (a.bounds(), b.bounds()) {
        // 平滑并会让过渡带稍微鼓出,包围盒按 k/2 外扩。
        (Some(x), Some(y)) =>
          Some(x.union(y).expand(if k > 0.0 { k * 0.5 } else { 0.0 }))
        _ => None
      }
    Intersect(a, b) =>
      match (a.bounds(), b.bounds()) {
        (Some(x), Some(y)) => {
          let r = x.intersect(y)
          if r.is_empty() {
            None
          } else {
            Some(r)
          }
        }
        (Some(x), None) => Some(x)
        (None, Some(y)) => Some(y)
        (None, None) => None
      }
    Subtract(a, _) => a.bounds()
  }
}