///|
pub(all) enum PathCommand2D {
  MoveTo(@math.Vec2)
  LineTo(@math.Vec2)
  QuadraticTo(@math.Vec2, @math.Vec2)
  CubicTo(@math.Vec2, @math.Vec2, @math.Vec2)
  Close
} derive(Eq, Debug)

///|
pub(all) struct PathGeometry2D {
  commands : Array[PathCommand2D]
  bounds_position : @math.Vec2
  bounds_size : @math.Vec2
} derive(Eq, Debug)

///|
pub(all) enum PathStretch2D {
  None
  Fill
  Uniform
} derive(Eq, Debug)

///|
pub(all) struct PathTessellation2D {
  fill_vertices : Array[@math.Vec2]
  stroke_vertices : Array[@math.Vec2]
} derive(Eq, Debug)

///|
priv struct PathContour2D {
  points : Array[@math.Vec2]
  mut closed : Bool
}

///|
fn path_point_transform(
  point : @math.Vec2,
  scale : @math.Vec2,
  offset : @math.Vec2,
) -> @math.Vec2 {
  @math.Vec2(point.0 * scale.0 + offset.0, point.1 * scale.1 + offset.1)
}

///|
fn path_flatten_contours(
  geometry : PathGeometry2D,
  scale : @math.Vec2,
  offset : @math.Vec2,
  curve_segments : Int,
) -> Array[PathContour2D] {
  let contours : Array[PathContour2D] = []
  let mut current : PathContour2D? = None
  let mut current_point = @math.Vec2::zero()
  let mut subpath_start = @math.Vec2::zero()
  for command in geometry.commands {
    match command {
      MoveTo(point) => {
        match current {
          Some(contour) if !contour.points.is_empty() => contours.push(contour)
          _ => ()
        }
        let transformed = path_point_transform(point, scale, offset)
        current = Some({ points: [transformed], closed: false })
        current_point = point
        subpath_start = point
      }
      LineTo(point) => {
        match current {
          Some(contour) =>
            contour.points.push(path_point_transform(point, scale, offset))
          None => ()
        }
        current_point = point
      }
      QuadraticTo(control, point) => {
        match current {
          Some(contour) =>
            for step in 1..<=curve_segments {
              let t = step.to_double() / curve_segments.to_double()
              let inverse = 1.0 - t
              let flattened = current_point.scalar_mul(inverse * inverse) +
                control.scalar_mul(2.0 * inverse * t) +
                point.scalar_mul(t * t)
              contour.points.push(
                path_point_transform(flattened, scale, offset),
              )
            }
          None => ()
        }
        current_point = point
      }
      CubicTo(control1, control2, point) => {
        match current {
          Some(contour) =>
            for step in 1..<=curve_segments {
              let t = step.to_double() / curve_segments.to_double()
              let inverse = 1.0 - t
              let flattened = current_point.scalar_mul(
                  inverse * inverse * inverse,
                ) +
                control1.scalar_mul(3.0 * inverse * inverse * t) +
                control2.scalar_mul(3.0 * inverse * t * t) +
                point.scalar_mul(t * t * t)
              contour.points.push(
                path_point_transform(flattened, scale, offset),
              )
            }
          None => ()
        }
        current_point = point
      }
      Close => {
        match current {
          Some(contour) => contour.closed = true
          None => ()
        }
        current_point = subpath_start
      }
    }
  }
  match current {
    Some(contour) if !contour.points.is_empty() => contours.push(contour)
    _ => ()
  }
  contours
}

///|
fn path_polygon_signed_area(points : Array[@math.Vec2]) -> Double {
  let mut area = 0.0
  for index in 0.. Bool {
  let ab = (b.0 - a.0) * (point.1 - a.1) - (b.1 - a.1) * (point.0 - a.0)
  let bc = (c.0 - b.0) * (point.1 - b.1) - (c.1 - b.1) * (point.0 - b.0)
  let ca = (a.0 - c.0) * (point.1 - c.1) - (a.1 - c.1) * (point.0 - c.0)
  let has_negative = ab < -0.0000001 || bc < -0.0000001 || ca < -0.0000001
  let has_positive = ab > 0.0000001 || bc > 0.0000001 || ca > 0.0000001
  !(has_negative && has_positive)
}

///|
fn path_triangulate_contour(points : Array[@math.Vec2]) -> Array[@math.Vec2] {
  if points.length() < 3 {
    return []
  }
  let winding = if path_polygon_signed_area(points) >= 0.0 { 1.0 } else { -1.0 }
  let remaining : Array[Int] = []
  for index in 0.. 3 &&
        guard_counter < points.length() * points.length() {
    let mut ear_index = -1
    for index in 0.. Array[@math.Vec2] {
  if contour.points.length() < 2 || stroke_width <= 0.0 {
    return []
  }
  let vertices : Array[@math.Vec2] = []
  let half_width = stroke_width * 0.5
  let segment_count = if contour.closed {
    contour.points.length()
  } else {
    contour.points.length() - 1
  }
  for index in 0.. PathTessellation2D {
  let physical_scale = output_scale.max(1.0)
  let largest_scale = scale.0.abs().max(scale.1.abs()).max(0.000001)
  let curve_segments = (8.0 +
    largest_scale *
    physical_scale *
    geometry.bounds_size.0.max(geometry.bounds_size.1) /
    12.0)
    .ceil()
    .to_int()
    .clamp(min=8, max=64)
  let contours = path_flatten_contours(geometry, scale, offset, curve_segments)
  let fill_vertices : Array[@math.Vec2] = []
  let stroke_vertices : Array[@math.Vec2] = []
  for contour in contours {
    if contour.closed {
      for vertex in path_triangulate_contour(contour.points) {
        fill_vertices.push(vertex)
      }
    }
    for vertex in path_stroke_contour(contour, stroke_width) {
      stroke_vertices.push(vertex)
    }
  }
  { fill_vertices, stroke_vertices }
}

///|
pub fn tessellate_colored_path_geometry(
  geometry : PathGeometry2D,
  scale : @math.Vec2,
  offset : @math.Vec2,
  stroke_width : Double,
  color : @render.Color,
  output_scale? : Double = 1.0,
) -> Array[ColoredTriangle2D] {
  let physical_scale = output_scale.max(1.0)
  let largest_scale = scale.0.abs().max(scale.1.abs()).max(0.000001)
  let curve_segments = (8.0 +
    largest_scale *
    physical_scale *
    geometry.bounds_size.0.max(geometry.bounds_size.1) /
    12.0)
    .ceil()
    .to_int()
    .clamp(min=8, max=64)
  let contours = path_flatten_contours(geometry, scale, offset, curve_segments)
  let triangles : Array[ColoredTriangle2D] = []
  let transparent_color = { ..color, a: 0.0 }
  let antialias_width = 1.0 / physical_scale
  for contour in contours {
    if contour.closed {
      let fill = path_triangulate_contour(contour.points)
      for index in 0..<(fill.length() / 3) {
        let start = index * 3
        triangles.push({
          a: { position: fill[start], color },
          b: { position: fill[start + 1], color },
          c: { position: fill[start + 2], color },
        })
      }
      let winding = if path_polygon_signed_area(contour.points) >= 0.0 {
        1.0
      } else {
        -1.0
      }
      for index in 0..