///|
// Exact conical-washer-face hex head with SURFACE_CURVE edges and pcurves.
//
// Topology: six planar hex flats bounded above by two hyperbolas each, six
// triangular chamfer patches (two hyperbolas + a top circle arc), a circular
// top face split into six arcs, and the hex underside. The chamfer is the
// golden's own representation: a 75 degree CONICAL_SURFACE per patch, trimmed
// by the flats. Every edge of a patch carries a pcurve on each of its two
// faces, so an importer can relate it to both surfaces without guessing.
//
// This imports with no face warnings and gives the exact reference volume
// (22.13797 mm3 for M2 against 22.1379 analytic, 475.479 mm3 for M5 x 20,
// matching the golden). The one prerequisite is that the global context
// declares the RADIAN plane-angle unit: `cad/context.mbt` used to declare
// DEGREE while the writer emitted radian-valued semi-angles, which made
// importers that honour the context read 75 as 75 degrees, rebuild every cone
// as a near cylinder and drop the chamfer faces.

///|
/// HHSArc is one circle level split into two half-arcs (u = 0 -> pi and
/// pi -> 2pi) with the vertices at +X and -X. Bands and annuli share these
/// edges, so no face needs a seam.
priv struct HHSArc {
  v0 : @step.Ref
  v1 : @step.Ref
  a : @step.Ref
  b : @step.Ref
}

///|
priv struct HHPlaneRef {
  surface : @step.Ref
  origin : Vec3
  axis : Vec3
  reference : Vec3
}

///|
/// HHConeRef is one chamfer's conical surface. The 75 degree washer-face cone
/// is trimmed by the six flats, so each of the six patches is a part of this
/// cone; the u-window per patch is small because each patch has its own
/// surface entity, referenced at the patch's leading top point.
priv struct HHConeRef {
  surface : @step.Ref
  origin : Vec3
  axis : Vec3
  reference : Vec3
}

///|
fn hhp_plane(
  ctx : StepContext,
  origin : Vec3,
  axis : Vec3,
  reference : Vec3,
) -> HHPlaneRef {
  let surface = hhwf_plane(ctx, origin, axis, reference)
  { surface, origin, axis: v_norm(axis), reference: v_norm(reference), }
}

///|
/// hhp_cone creates the chamfer's CONICAL_SURFACE. `hhwf_cone` converts its
/// semi-angle to radians, which is what the global context's plane-angle unit
/// declares, so the emitted cone is read as 75 degrees by every importer.
fn hhp_cone(
  ctx : StepContext,
  origin : Vec3,
  axis : Vec3,
  reference : Vec3,
  radius : Double,
  semi_angle_deg : Double,
) -> HHConeRef {
  let surface = hhwf_cone(ctx, origin, axis, reference, radius, semi_angle_deg)
  { surface, origin, axis: v_norm(axis), reference: v_norm(reference), }
}

///|
/// hhp_cone_uv maps a point into the cone's parameter space (u = angle from the
/// patch's reference direction, v = distance along the cone's axis).
fn hhp_cone_uv(p : Vec3, cone : HHConeRef) -> (Double, Double) {
  let xdir = cone.reference
  let ydir = v_norm(v_cross(cone.axis, cone.reference))
  let d = v_sub(p, cone.origin)
  let u = @math.atan2(v_dot(d, ydir), v_dot(d, xdir))
  let v = v_dot(d, cone.axis)
  (u, v)
}

///|
fn hhp_plane_uv(p : Vec3, plane : HHPlaneRef) -> (Double, Double) {
  let xdir = plane.reference
  let ydir = v_norm(v_cross(plane.axis, plane.reference))
  let d = v_sub(p, plane.origin)
  (v_dot(d, xdir), v_dot(d, ydir))
}

///|

///|
fn hhp_pcurve_2d_line(
  ctx : StepContext,
  surface : @step.Ref,
  a : (Double, Double),
  b : (Double, Double),
  context_2d : @step.Ref,
) -> @step.Ref {
  let du = b.0 - a.0
  let dv = b.1 - a.1
  let len = Double::sqrt(du * du + dv * dv)
  let direction = if len == 0.0 { (1.0, 0.0) } else { (du / len, dv / len) }
  let pnt = ctx.cartesian_point_2d(a.0, a.1)
  let dir = ctx.add(
    @repository.any_entity_direction(
      @geometry.Direction::new("", [direction.0, direction.1]),
    ),
  )
  let vec = ctx.add(
    @repository.any_entity_vector(@geometry.Vector::new("", dir, 1.0)),
  )
  let line = ctx.add(
    @repository.any_entity_line(@geometry.Line::new("", pnt, vec)),
  )
  let rep = ctx.definitional_representation([line], context_2d)
  ctx.pcurve(surface, rep)
}

///|
/// hhp_pcurve_circle writes an exact 2D circle pcurve (the reference files'
/// representation for the top arcs on the top plane).
fn hhp_pcurve_circle(
  ctx : StepContext,
  surface : @step.Ref,
  center_u : Double,
  center_v : Double,
  radius : Double,
  context_2d : @step.Ref,
) -> @step.Ref {
  let location = ctx.cartesian_point_2d(center_u, center_v)
  let ref_dir = ctx.add(
    @repository.any_entity_direction(@geometry.Direction::new("", [1.0, 0.0])),
  )
  let placement = @geometry.Axis2Placement2D::new(
    "",
    location,
    ref_direction=Some(ref_dir),
  )
  let placement_ref = ctx.add(
    @repository.any_entity_axis2_placement_2d(placement),
  )
  let circle = ctx.add(
    @repository.any_entity_circle(
      @geometry.Circle::new("", placement_ref, @step.fmt_num(radius)),
    ),
  )
  let rep = ctx.definitional_representation([circle], context_2d)
  ctx.pcurve(surface, rep)
}

///|
/// hhp_pcurve_hyperbola writes the flat/cone intersection as a 2D hyperbola.
/// The flat plane's u axis is the tangential direction and its v axis points
/// down, so the transverse axis is +v: ref_direction (0, 1), semi-axis along v.
fn hhp_pcurve_hyperbola(
  ctx : StepContext,
  surface : @step.Ref,
  center_u : Double,
  center_v : Double,
  semi_axis : Double,
  semi_imag : Double,
  context_2d : @step.Ref,
) -> @step.Ref {
  let location = ctx.cartesian_point_2d(center_u, center_v)
  let ref_dir = ctx.add(
    @repository.any_entity_direction(@geometry.Direction::new("", [0.0, 1.0])),
  )
  let placement = @geometry.Axis2Placement2D::new(
    "",
    location,
    ref_direction=Some(ref_dir),
  )
  let placement_ref = ctx.add(
    @repository.any_entity_axis2_placement_2d(placement),
  )
  let hyperbola = ctx.add(
    @repository.any_entity_hyperbola(
      @geometry.Hyperbola::new("", placement_ref, semi_axis, semi_imag),
    ),
  )
  let rep = ctx.definitional_representation([hyperbola], context_2d)
  ctx.pcurve(surface, rep)
}

///|
fn hhp_all_collinear(uv : Array[(Double, Double)]) -> Bool {
  let n = uv.length()
  if n < 2 {
    return false
  }
  let du = uv[n - 1].0 - uv[0].0
  let dv = uv[n - 1].1 - uv[0].1
  let len = Double::sqrt(du * du + dv * dv)
  if len < 1.0e-12 {
    return false
  }
  for (u, v) in uv {
    let cross = (u - uv[0].0) * dv - (v - uv[0].1) * du
    if Double::abs(cross) / len > 1.0e-9 {
      return false
    }
  }
  true
}

///|
fn hhp_asinh(x : Double) -> Double {
  @math.ln(x + Double::sqrt(x * x + 1.0))
}

///|
fn hhp_pcurve_polyline(
  ctx : StepContext,
  surface : @step.Ref,
  uv : Array[(Double, Double)],
  context_2d : @step.Ref,
  params : Array[Double]?,
) -> @step.Ref {
  let n = uv.length()
  if n < 2 {
    return ctx.pcurve(surface, ctx.definitional_representation([], context_2d))
  }
  // A degree-1 LINE pcurve is only equivalent when the pcurve parameter is the
  // distance along it. With explicit parameters the knots must be those
  // parameters (the 3D curve's own parameter values), so keep the B-spline.
  if hhp_all_collinear(uv) && params is None {
    return hhp_pcurve_2d_line(ctx, surface, uv[0], uv[n - 1], context_2d)
  }
  let cps : Array[@step.Ref] = []
  let mut previous_u = 0.0
  for i, (u, v) in uv {
    let mut uu = u
    if i > 0 {
      while uu - previous_u > @math.PI {
        uu = uu - 2.0 * @math.PI
      }
      while uu - previous_u < -@math.PI {
        uu = uu + 2.0 * @math.PI
      }
    }
    previous_u = uu
    cps.push(ctx.cartesian_point_2d(uu, v))
  }
  let mults : Array[Int32] = [2]
  for _ in 1..<(n - 1) {
    mults.push(1)
  }
  mults.push(2)
  let knots : Array[String] = []
  match params {
    Some(ps) =>
      if ps.length() == n {
        for p in ps {
          knots.push(@step.fmt_num(p))
        }
      } else {
        for i in 0..
      for i in 0.. end
/// order; `edge_forward` says whether that order agrees with the 3D curve's own
/// parameter direction. Pcurves always follow the 3D curve's parameterization,
/// so they are reversed when the edge runs against it.
fn hhp_surface_edge(
  ctx : StepContext,
  curve_3d : @step.Ref,
  samples : Array[Vec3],
  plane_a : HHPlaneRef,
  plane_b : HHPlaneRef?,
  cone_b : HHConeRef?,
  context_2d : @step.Ref,
  start_vp : @step.Ref,
  end_vp : @step.Ref,
  flat_hyperbola? : (Double, Double, Double, Double),
  plane_a_circle? : (Double, Double, Double),
  params? : Array[Double],
  edge_forward~ : Bool = true,
) -> @step.Ref {
  let pc_samples = if edge_forward { samples } else { samples.rev() }
  let pc_params = match params {
    Some(ps) => if edge_forward { Some(ps) } else { Some(ps.rev()) }
    None => None
  }
  let uv_a = pc_samples.map(fn(p) { hhp_plane_uv(p, plane_a) })
  let pc_a = match flat_hyperbola {
    Some((cu, cv, sa, sb)) =>
      hhp_pcurve_hyperbola(ctx, plane_a.surface, cu, cv, sa, sb, context_2d)
    None =>
      match plane_a_circle {
        Some((cu, cv, r)) =>
          hhp_pcurve_circle(ctx, plane_a.surface, cu, cv, r, context_2d)
        None =>
          hhp_pcurve_polyline(ctx, plane_a.surface, uv_a, context_2d, pc_params)
      }
  }
  let assoc : Array[@step.Ref] = [pc_a]
  match plane_b {
    Some(pb) => {
      let uv_b = pc_samples.map(fn(p) { hhp_plane_uv(p, pb) })
      assoc.push(
        hhp_pcurve_polyline(ctx, pb.surface, uv_b, context_2d, pc_params),
      )
    }
    None =>
      match cone_b {
        Some(cb) => {
          let uv_b = pc_samples.map(fn(p) { hhp_cone_uv(p, cb) })
          assoc.push(
            hhp_pcurve_polyline(ctx, cb.surface, uv_b, context_2d, pc_params),
          )
        }
        None => ()
      }
  }
  let sc = ctx.surface_curve(curve_3d, assoc)
  ctx.edge_curve(start_vp, end_vp, sc, edge_forward)
}

///|
/// hhp_hyperbola_samples samples one half of the flat/cone hyperbola. `side` is
/// +1 for the half towards the next base vertex and -1 for the half towards the
/// previous one; `from_base` selects the traversal direction.
fn hhp_hyperbola_samples(
  phi : Double,
  rt : Double,
  tan_alpha : Double,
  zb : Double,
  k : Double,
  side : Double,
  from_base : Bool,
  xform : Transform,
) -> Array[Vec3] {
  let tangent = (-@math.sin(phi), @math.cos(phi), 0.0)
  let n = 48
  let pts : Array[Vec3] = []
  for i in 0..<=n {
    let t = i.to_double() / n.to_double()
    let z = if from_base { zb + (k - zb) * t } else { k - (k - zb) * t }
    let w = k - z
    let u = Double::sqrt(
      2.0 * rt * tan_alpha * w + tan_alpha * tan_alpha * w * w,
    )
    let p_local = (
      rt * @math.cos(phi) + side * u * tangent.0,
      rt * @math.sin(phi) + side * u * tangent.1,
      z,
    )
    pts.push(xform.apply(p_local))
  }
  pts
}

///|
/// compile_hex_head_pcurves emits the exact conical-washer-face head.
pub fn compile_hex_head_pcurves(
  ctx : StepContext,
  node : SceneNode,
  h : HexHeadWasherFace,
  reverse~ : Bool = false,
  parent~ : Transform? = None,
) -> Array[@step.Ref] {
  let g = match hhwf_geometry(h) {
    Some(g) => g
    None => return []
  }
  let xform = node_transform(node, parent)
  let up = xform.apply_dir((0.0, 0.0, 1.0))
  let down = xform.apply_dir((0.0, 0.0, -1.0))
  let right = xform.apply_dir((1.0, 0.0, 0.0))
  let dir_up = ctx.direction(up.0, up.1, up.2)
  let dir_right = ctx.direction(right.0, right.1, right.2)
  let context_2d = ctx.context_2d()

  // Vertices.
  let base_pos : Array[Vec3] = []
  let mid_pos : Array[Vec3] = []
  let top_pos : Array[Vec3] = []
  for i in 0..<6 {
    let a = 60.0 * i.to_double() * @math.PI / 180.0
    let b = (30.0 + 60.0 * i.to_double()) * @math.PI / 180.0
    base_pos.push(
      xform.apply((g.rb * @math.cos(a), g.rb * @math.sin(a), g.z_base)),
    )
    mid_pos.push(xform.apply((g.rb * @math.cos(a), g.rb * @math.sin(a), g.zb)))
    top_pos.push(xform.apply((g.rt * @math.cos(b), g.rt * @math.sin(b), g.k)))
  }
  let base_cp = base_pos.map(fn(p) { hhwf_cp(ctx, p) })
  let mid_cp = mid_pos.map(fn(p) { hhwf_cp(ctx, p) })
  let top_cp = top_pos.map(fn(p) { hhwf_cp(ctx, p) })
  let base_vp = base_cp.map(fn(c) { ctx.vertex_point(c) })
  let mid_vp = mid_cp.map(fn(c) { ctx.vertex_point(c) })
  let top_vp = top_cp.map(fn(c) { ctx.vertex_point(c) })

  // Surfaces.
  let underside = hhp_plane(ctx, xform.apply((0.0, 0.0, g.z_base)), down, right)
  let top_plane = hhp_plane(ctx, xform.apply((0.0, 0.0, g.k)), up, right)
  let flat_planes : Array[HHPlaneRef] = []
  for i in 0..<6 {
    let phi = (30.0 + 60.0 * i.to_double()) * @math.PI / 180.0
    let normal = v_norm(xform.apply_dir((@math.cos(phi), @math.sin(phi), 0.0)))
    let midpoint = xform.apply(
      (g.rt * @math.cos(phi), g.rt * @math.sin(phi), 0.0),
    )
    // The plane's u axis runs backwards along the tangent (towards decreasing
    // azimuth) and its v axis points down (-Z). That frame makes the flat/cone
    // intersection a 2D hyperbola with a positive semi-axis, exactly as the
    // reference files write it.
    let ref_dir = v_norm(
      xform.apply_dir((@math.sin(phi), -@math.cos(phi), 0.0)),
    )
    flat_planes.push(hhp_plane(ctx, midpoint, normal, ref_dir))
  }
  // One conical surface per chamfer patch, referenced at the patch's leading
  // top point, so each patch's pcurves sit in a small, seam-free u-window.
  let cones : Array[HHConeRef] = []
  for i in 0..<6 {
    let lead = (60.0 * i.to_double() - 30.0) * @math.PI / 180.0
    cones.push(
      hhp_cone(
        ctx,
        xform.apply((0.0, 0.0, g.zb)),
        v_norm(xform.apply_dir((0.0, 0.0, -1.0))),
        v_norm(xform.apply_dir((@math.cos(lead), @math.sin(lead), 0.0))),
        g.rb,
        g.face_angle_deg,
      ),
    )
  }

  // Bottom hex edges.
  let bottom_edges : Array[@step.Ref] = []
  for i in 0..<6 {
    let j = (i + 1) % 6
    let curve = ctx.add(
      @repository.any_entity_line(
        @geometry.Line::new(
          "",
          base_cp[i],
          ctx.direction(
            v_norm(v_sub(base_pos[j], base_pos[i])).0,
            v_norm(v_sub(base_pos[j], base_pos[i])).1,
            v_norm(v_sub(base_pos[j], base_pos[i])).2,
          )
          |> d => { ctx.vector(d, 1.0) },
        ),
      ),
    )
    bottom_edges.push(
      hhp_surface_edge(
        ctx,
        curve,
        [base_pos[i], base_pos[j]],
        underside,
        Some(flat_planes[i]),
        None,
        context_2d,
        base_vp[i],
        base_vp[j],
      ),
    )
  }
  // Vertical hex edges at each vertex.
  let vertical_edges : Array[@step.Ref] = []
  for i in 0..<6 {
    let dir = v_norm(v_sub(mid_pos[i], base_pos[i]))
    let curve = ctx.add(
      @repository.any_entity_line(
        @geometry.Line::new(
          "",
          base_cp[i],
          ctx.direction(dir.0, dir.1, dir.2) |> d => { ctx.vector(d, 1.0) },
        ),
      ),
    )
    vertical_edges.push(
      hhp_surface_edge(
        ctx,
        curve,
        [base_pos[i], mid_pos[i]],
        flat_planes[(i + 5) % 6],
        Some(flat_planes[i]),
        None,
        context_2d,
        base_vp[i],
        mid_vp[i],
      ),
    )
  }
  // Hyperbola edges: left and right of each flat.
  let hyper_left : Array[@step.Ref] = []
  let hyper_right : Array[@step.Ref] = []
  for i in 0..<6 {
    let phi = (30.0 + 60.0 * i.to_double()) * @math.PI / 180.0
    let center = xform.apply(
      (g.rt * @math.cos(phi), g.rt * @math.sin(phi), g.k + g.rt / g.tan_alpha),
    )
    // Outward axis and a downward reference, as in the reference files: the
    // curve's Y axis then runs along +tangent, so the parameter grows from
    // the top tangency point towards either base vertex.
    let axis = v_norm(xform.apply_dir((@math.cos(phi), @math.sin(phi), 0.0)))
    let reference = v_norm(xform.apply_dir((0.0, 0.0, -1.0)))
    let a2p = ctx.axis2_placement_3d(
      hhwf_cp(ctx, center),
      ctx.direction(axis.0, axis.1, axis.2),
      ctx.direction(reference.0, reference.1, reference.2),
    )
    let hyperbola = ctx.add(
      @repository.any_entity_hyperbola(
        @geometry.Hyperbola::new("", a2p, g.rt / g.tan_alpha, g.rt),
      ),
    )
    // The flat plane's u axis is -tangent, so the hyperbola parameter is
    // asinh(-u / rt).
    let params_for = fn(samples : Array[Vec3]) -> Array[Double] {
      samples.map(fn(p) { hhp_asinh(-hhp_plane_uv(p, flat_planes[i]).0 / g.rt) })
    }
    // Left half: top tangency point i -> base vertex i.
    let left_samples = hhp_hyperbola_samples(
      phi,
      g.rt,
      g.tan_alpha,
      g.zb,
      g.k,
      -1.0,
      false,
      xform,
    )
    let left_params = params_for(left_samples)
    hyper_left.push(
      hhp_surface_edge(
        ctx,
        hyperbola,
        left_samples,
        flat_planes[i],
        None,
        Some(cones[i]),
        context_2d,
        top_vp[i],
        mid_vp[i],
        flat_hyperbola=(
          0.0,
          -(g.k + g.rt / g.tan_alpha),
          g.rt / g.tan_alpha,
          g.rt,
        ),
        params=left_params,
        edge_forward=false,
      ),
    )
    // Right half: base vertex i+1 -> top tangency point i.
    let right_samples = hhp_hyperbola_samples(
      phi,
      g.rt,
      g.tan_alpha,
      g.zb,
      g.k,
      1.0,
      true,
      xform,
    )
    let right_params = params_for(right_samples)
    hyper_right.push(
      hhp_surface_edge(
        ctx,
        hyperbola,
        right_samples,
        flat_planes[i],
        None,
        Some(cones[(i + 1) % 6]),
        context_2d,
        mid_vp[(i + 1) % 6],
        top_vp[i],
        flat_hyperbola=(
          0.0,
          -(g.k + g.rt / g.tan_alpha),
          g.rt / g.tan_alpha,
          g.rt,
        ),
        params=right_params,
        edge_forward=false,
      ),
    )
  }
  // Top circle arcs.
  let top_edges : Array[@step.Ref] = []
  for i in 0..<6 {
    let j = (i + 1) % 6
    let a0 = (30.0 + 60.0 * i.to_double()) * @math.PI / 180.0
    // Always sweep forwards by one sixth of a turn; computing the end angle
    // from the wrapped vertex index would run the last arc backwards and
    // produce decreasing pcurve knots.
    let a1 = a0 + @math.PI / 3.0
    let center_cp = hhwf_cp(ctx, xform.apply((0.0, 0.0, g.k)))
    let a2p = ctx.axis2_placement_3d(center_cp, dir_up, dir_right)
    let circle = ctx.add(
      @repository.any_entity_circle(
        @geometry.Circle::new("", a2p, @step.fmt_num(g.rt)),
      ),
    )
    let samples : Array[Vec3] = []
    let angles : Array[Double] = []
    for s in 0..<=12 {
      let t = s.to_double() / 12.0
      let a = a0 + (a1 - a0) * t
      samples.push(xform.apply((g.rt * @math.cos(a), g.rt * @math.sin(a), g.k)))
      angles.push(a)
    }
    top_edges.push(
      hhp_surface_edge(
        ctx,
        circle,
        samples,
        top_plane,
        None,
        Some(cones[(i + 1) % 6]),
        context_2d,
        top_vp[i],
        top_vp[j],
        plane_a_circle=(0.0, 0.0, g.rt),
        params=angles,
      ),
    )
  }

  let faces : Array[@step.Ref] = []
  // Flat faces.
  for i in 0..<6 {
    let j = (i + 1) % 6
    faces.push(
      hhwf_face(
        ctx,
        [
          (bottom_edges[i], true),
          (vertical_edges[j], true),
          (hyper_right[i], true),
          (hyper_left[i], true),
          (vertical_edges[i], false),
        ],
        flat_planes[i].surface,
        flat_planes[i].axis,
        [base_pos[i], base_pos[j], mid_pos[j], top_pos[i], mid_pos[i]],
      ),
    )
  }
  // Cone faces.
  for i in 0..<6 {
    // The reference cone faces use the same sense as their conical surface
    // and all three boundary edges reversed; do the same explicitly.
    let cone_loop = ctx.edge_loop([
      ctx.oriented_edge(hyper_left[i], false),
      ctx.oriented_edge(top_edges[(i + 5) % 6], false),
      ctx.oriented_edge(hyper_right[(i + 5) % 6], false),
    ])
    faces.push(
      ctx.advanced_face(
        [ctx.face_outer_bound(cone_loop, true)],
        cones[i].surface,
        !reverse,
      ),
    )
  }
  // Underside. Head-only: the full hex underside (opposite the flats' bottom
  // edges), reusing the plane entity the bottom edges' pcurves reference.
  // With a shank: an annular underside (or under-head washer face), a land
  // cylinder, and the shank with its tip break, all in the same closed shell.
  let has_shank = h.shank_diameter_mm > 0.0 && h.shank_length_mm > 0.0
  let hex_oriented : Array[@step.Ref] = []
  for i in 0..<6 {
    hex_oriented.push(ctx.oriented_edge(bottom_edges[(5 - i) % 6], false))
  }
  if !has_shank {
    let underside_oriented : Array[(@step.Ref, Bool)] = []
    let underside_points : Array[Vec3] = []
    for i in 0..<6 {
      underside_oriented.push((bottom_edges[(5 - i) % 6], false))
      underside_points.push(base_pos[(5 - i) % 6])
    }
    faces.push(
      hhwf_face(
        ctx,
        underside_oriented,
        underside.surface,
        underside.axis,
        underside_points,
      ),
    )
  } else {
    let r_h = h.shank_diameter_mm / 2.0
    let r_w = h.washer_face_diameter_mm / 2.0
    let has_washer = r_w > 0.0 && h.washer_face_height_mm > 0.0
    let length = h.shank_length_mm
    let c_tip = if h.tip_chamfer_mm > 0.0 && h.tip_chamfer_mm < r_h {
      h.tip_chamfer_mm
    } else {
      0.0
    }
    let z_shank_bot = -length + c_tip
    let z_tip = -length
    let half_level = fn(z : Double, r : Double) -> HHSArc {
      let center_cp = hhwf_cp(ctx, xform.apply((0.0, 0.0, z)))
      let a2p = ctx.axis2_placement_3d(center_cp, dir_up, dir_right)
      let circle = ctx.add(
        @repository.any_entity_circle(
          @geometry.Circle::new("", a2p, @step.fmt_num(r)),
        ),
      )
      let cp0 = hhwf_cp(ctx, xform.apply((r, 0.0, z)))
      let cp1 = hhwf_cp(ctx, xform.apply((-r, 0.0, z)))
      let v0 = ctx.vertex_point(cp0)
      let v1 = ctx.vertex_point(cp1)
      {
        v0,
        v1,
        a: ctx.edge_curve(v0, v1, circle, true),
        b: ctx.edge_curve(v1, v0, circle, true),
      }
    }
    let cylinder_at = fn(z : Double, r : Double) -> @step.Ref {
      let center_cp = hhwf_cp(ctx, xform.apply((0.0, 0.0, z)))
      let a2p = ctx.axis2_placement_3d(center_cp, dir_up, dir_right)
      ctx.add(
        @repository.any_entity_cylindrical_surface(
          @geometry.CylindricalSurface::new("", a2p, @step.fmt_num(r)),
        ),
      )
    }
    let band = fn(
      z0 : Double,
      r0 : Double,
      lo : HHSArc,
      z1 : Double,
      r1 : Double,
      hi : HHSArc,
      surface : @step.Ref,
    ) -> Unit {
      let dir0 = v_norm(
        v_sub(xform.apply((r1, 0.0, z1)), xform.apply((r0, 0.0, z0))),
      )
      let dir1 = v_norm(
        v_sub(xform.apply((-r1, 0.0, z1)), xform.apply((-r0, 0.0, z0))),
      )
      let seam0 = ctx.edge_curve(
        lo.v0,
        hi.v0,
        ctx.line(
          hhwf_cp(ctx, xform.apply((r0, 0.0, z0))),
          ctx.vector(ctx.direction(dir0.0, dir0.1, dir0.2), 1.0),
        ),
        true,
      )
      let seam1 = ctx.edge_curve(
        lo.v1,
        hi.v1,
        ctx.line(
          hhwf_cp(ctx, xform.apply((-r0, 0.0, z0))),
          ctx.vector(ctx.direction(dir1.0, dir1.1, dir1.2), 1.0),
        ),
        true,
      )
      let loop_a = ctx.edge_loop([
        ctx.oriented_edge(seam0, true),
        ctx.oriented_edge(hi.a, true),
        ctx.oriented_edge(seam1, false),
        ctx.oriented_edge(lo.a, false),
      ])
      let loop_b = ctx.edge_loop([
        ctx.oriented_edge(seam1, true),
        ctx.oriented_edge(hi.b, true),
        ctx.oriented_edge(seam0, false),
        ctx.oriented_edge(lo.b, false),
      ])
      faces.push(
        ctx.advanced_face(
          [ctx.face_outer_bound(loop_a, true)],
          surface,
          reverse,
        ),
      )
      faces.push(
        ctx.advanced_face(
          [ctx.face_outer_bound(loop_b, true)],
          surface,
          reverse,
        ),
      )
    }
    let hex_loop = ctx.edge_loop(hex_oriented)
    let tip_plane = hhp_plane(ctx, xform.apply((0.0, 0.0, z_tip)), down, right)
    let l_top = half_level(0.0, r_h)
    let l_bot = half_level(z_shank_bot, r_h)
    if has_washer {
      let under0 = hhp_plane(ctx, xform.apply((0.0, 0.0, 0.0)), down, right)
      let l_wtop = half_level(h.washer_face_height_mm, r_w)
      let l_wbot = half_level(0.0, r_w)
      faces.push(
        ctx.advanced_face(
          [
            ctx.face_outer_bound(hex_loop, true),
            ctx.face_bound(
              ctx.edge_loop([
                ctx.oriented_edge(l_wtop.a, false),
                ctx.oriented_edge(l_wtop.b, false),
              ]),
              true,
            ),
          ],
          underside.surface,
          !reverse,
        ),
      )
      band(
        0.0,
        r_w,
        l_wbot,
        h.washer_face_height_mm,
        r_w,
        l_wtop,
        cylinder_at(0.0, r_w),
      )
      faces.push(
        ctx.advanced_face(
          [
            ctx.face_outer_bound(
              ctx.edge_loop([
                ctx.oriented_edge(l_wbot.a, true),
                ctx.oriented_edge(l_wbot.b, true),
              ]),
              true,
            ),
            ctx.face_bound(
              ctx.edge_loop([
                ctx.oriented_edge(l_top.a, false),
                ctx.oriented_edge(l_top.b, false),
              ]),
              true,
            ),
          ],
          under0.surface,
          reverse,
        ),
      )
    } else {
      faces.push(
        ctx.advanced_face(
          [
            ctx.face_outer_bound(hex_loop, true),
            ctx.face_bound(
              ctx.edge_loop([
                ctx.oriented_edge(l_top.a, false),
                ctx.oriented_edge(l_top.b, false),
              ]),
              true,
            ),
          ],
          underside.surface,
          !reverse,
        ),
      )
    }
    band(
      z_shank_bot,
      r_h,
      l_bot,
      0.0,
      r_h,
      l_top,
      cylinder_at(z_shank_bot, r_h),
    )
    if c_tip > 0.0 {
      let tip_r = r_h - c_tip
      let l_tip = half_level(z_tip, tip_r)
      band(
        z_tip,
        tip_r,
        l_tip,
        z_shank_bot,
        r_h,
        l_bot,
        hhwf_cone(
          ctx,
          xform.apply((0.0, 0.0, z_shank_bot - r_h)),
          up,
          right,
          0.0,
          45.0,
        ),
      )
      faces.push(
        ctx.advanced_face(
          [
            ctx.face_outer_bound(
              ctx.edge_loop([
                ctx.oriented_edge(l_tip.a, true),
                ctx.oriented_edge(l_tip.b, true),
              ]),
              true,
            ),
          ],
          tip_plane.surface,
          reverse,
        ),
      )
    } else {
      faces.push(
        ctx.advanced_face(
          [
            ctx.face_outer_bound(
              ctx.edge_loop([
                ctx.oriented_edge(l_bot.a, true),
                ctx.oriented_edge(l_bot.b, true),
              ]),
              true,
            ),
          ],
          tip_plane.surface,
          reverse,
        ),
      )
    }
  }
  // Top circle (forward, opposite the cone faces' top arcs). As above, the
  // face reuses the top plane that the arcs' pcurves reference.
  let top_loop = ctx.edge_loop(
    top_edges.map(fn(e) { ctx.oriented_edge(e, true) }),
  )
  faces.push(
    ctx.advanced_face(
      [ctx.face_outer_bound(top_loop, true)],
      top_plane.surface,
      !reverse,
    ),
  )
  faces
}