package draw

import "core:math"

// ----- Internal helpers ----

// Internal
extrude_line :: proc(
	start, end_pos: Vec2,
	thickness: f32,
	color: Color,
	vertices: []Vertex,
	offset: int,
) -> int {
	direction := end_pos - start
	delta_x := direction[0]
	delta_y := direction[1]
	length := math.sqrt(delta_x * delta_x + delta_y * delta_y)
	if length < 0.0001 do return 0

	scale := thickness / (2 * length)
	perpendicular := Vec2{-delta_y * scale, delta_x * scale}

	p0 := start + perpendicular
	p1 := start - perpendicular
	p2 := end_pos - perpendicular
	p3 := end_pos + perpendicular

	vertices[offset + 0] = solid_vertex(p0, color)
	vertices[offset + 1] = solid_vertex(p1, color)
	vertices[offset + 2] = solid_vertex(p2, color)
	vertices[offset + 3] = solid_vertex(p0, color)
	vertices[offset + 4] = solid_vertex(p2, color)
	vertices[offset + 5] = solid_vertex(p3, color)

	return 6
}

// Create a vertex for solid-color shape drawing (no texture, UV defaults to zero).
// Color is premultiplied: the tessellated fragment shader passes it through directly
// and the blend state is ONE, ONE_MINUS_SRC_ALPHA.
solid_vertex :: proc(position: Vec2, color: Color) -> Vertex {
	return Vertex{position = position, color = premultiply_color(color)}
}

emit_rectangle :: proc(x, y, width, height: f32, color: Color, vertices: []Vertex, offset: int) {
	vertices[offset + 0] = solid_vertex({x, y}, color)
	vertices[offset + 1] = solid_vertex({x + width, y}, color)
	vertices[offset + 2] = solid_vertex({x + width, y + height}, color)
	vertices[offset + 3] = solid_vertex({x, y}, color)
	vertices[offset + 4] = solid_vertex({x + width, y + height}, color)
	vertices[offset + 5] = solid_vertex({x, y + height}, color)
}

// Internal
prepare_sdf_primitive_textured :: proc(
	layer: ^Layer,
	prim: Primitive,
	texture_id: Texture_Id,
	sampler: Sampler_Preset,
) {
	offset := u32(len(GLOB.tmp_primitives))
	append(&GLOB.tmp_primitives, prim)
	scissor := &GLOB.scissors[layer.scissor_start + layer.scissor_len - 1]
	append_or_extend_sub_batch(scissor, layer, .SDF, offset, 1, texture_id, sampler)
}

//Internal
//
// Compute the visual center of a center-parametrized shape after applying
// Convention B origin semantics: `center` is where the origin-point lands in
// world space; the visual center is offset by -origin and then rotated around
// the landing point.
//   visual_center = center + R(θ) · (-origin)
// When θ=0: visual_center = center - origin (pure positioning shift).
// When origin={0,0}: visual_center = center (no change).
compute_pivot_center :: proc(center: Vec2, origin: Vec2, sin_angle, cos_angle: f32) -> Vec2 {
	if origin == {0, 0} do return center
	return(
		center +
		{cos_angle * (-origin.x) - sin_angle * (-origin.y), sin_angle * (-origin.x) + cos_angle * (-origin.y)} \
	)
}

// Compute the AABB half-extents of a rectangle with half-size (half_width, half_height) rotated by the given cos/sin.
rotated_aabb_half_extents :: proc(half_width, half_height, cos_angle, sin_angle: f32) -> [2]f32 {
	cos_abs := abs(cos_angle)
	sin_abs := abs(sin_angle)
	return {half_width * cos_abs + half_height * sin_abs, half_width * sin_abs + half_height * cos_abs}
}

// Pack sin/cos into the Primitive.rotation_sc field as two f16 values.
pack_rotation_sc :: #force_inline proc(sin_angle, cos_angle: f32) -> u32 {
	return pack_f16_pair(f16(sin_angle), f16(cos_angle))
}


// Internal
//
// Build an RRect Primitive with bounds, params, and rotation computed from rectangle geometry.
// The caller sets color, flags, and uv fields on the returned primitive before submitting.
build_rrect_primitive :: proc(
	rect: Rectangle,
	radii: Rectangle_Radii,
	origin: Vec2,
	rotation: f32,
	feather_px: f32,
) -> Primitive {
	max_radius := min(rect.width, rect.height) * 0.5
	clamped_top_left := clamp(radii.top_left, 0, max_radius)
	clamped_top_right := clamp(radii.top_right, 0, max_radius)
	clamped_bottom_right := clamp(radii.bottom_right, 0, max_radius)
	clamped_bottom_left := clamp(radii.bottom_left, 0, max_radius)

	half_feather := feather_px * 0.5
	padding := half_feather / GLOB.dpi_scaling
	dpi_scale := GLOB.dpi_scaling

	half_width := rect.width * 0.5
	half_height := rect.height * 0.5
	center_x := rect.x + half_width - origin.x
	center_y := rect.y + half_height - origin.y
	sin_angle: f32 = 0
	cos_angle: f32 = 1
	has_rotation := false

	if needs_transform(origin, rotation) {
		rotation_radians := math.to_radians(rotation)
		sin_angle, cos_angle = math.sincos(rotation_radians)
		has_rotation = rotation != 0
		transform := build_pivot_rotation_sc({rect.x + origin.x, rect.y + origin.y}, origin, cos_angle, sin_angle)
		new_center := apply_transform(transform, {half_width, half_height})
		center_x = new_center.x
		center_y = new_center.y
	}

	bounds_half_width, bounds_half_height := half_width, half_height
	if has_rotation {
		expanded := rotated_aabb_half_extents(half_width, half_height, cos_angle, sin_angle)
		bounds_half_width = expanded.x
		bounds_half_height = expanded.y
	}

	prim := Primitive {
		bounds      = {
			center_x - bounds_half_width - padding,
			center_y - bounds_half_height - padding,
			center_x + bounds_half_width + padding,
			center_y + bounds_half_height + padding,
		},
		rotation_sc = has_rotation ? pack_rotation_sc(sin_angle, cos_angle) : 0,
	}
	prim.params.rrect = RRect_Params {
		half_size    = {half_width * dpi_scale, half_height * dpi_scale},
		radii        = {
			clamped_bottom_right * dpi_scale,
			clamped_top_right * dpi_scale,
			clamped_bottom_left * dpi_scale,
			clamped_top_left * dpi_scale,
		},
		half_feather = half_feather,
	}
	return prim
}

// Internal
//
// Build an RRect Primitive for a circle (fully-rounded square RRect).
// The caller sets color, flags, and uv fields on the returned primitive before submitting.
build_circle_primitive :: proc(
	center: Vec2,
	radius: f32,
	origin: Vec2,
	rotation: f32,
	feather_px: f32,
) -> Primitive {
	half_feather := feather_px * 0.5
	padding := half_feather / GLOB.dpi_scaling
	dpi_scale := GLOB.dpi_scaling

	actual_center := center
	if origin != {0, 0} {
		sin_a, cos_a := math.sincos(math.to_radians(rotation))
		actual_center = compute_pivot_center(center, origin, sin_a, cos_a)
	}

	prim := Primitive {
		bounds = {
			actual_center.x - radius - padding,
			actual_center.y - radius - padding,
			actual_center.x + radius + padding,
			actual_center.y + radius + padding,
		},
	}
	scaled_radius := radius * dpi_scale
	prim.params.rrect = RRect_Params {
		half_size    = {scaled_radius, scaled_radius},
		radii        = {scaled_radius, scaled_radius, scaled_radius, scaled_radius},
		half_feather = half_feather,
	}
	return prim
}

// Internal
//
// Build an Ellipse Primitive with bounds, params, and rotation computed from ellipse geometry.
// The caller sets color, flags, and uv fields on the returned primitive before submitting.
build_ellipse_primitive :: proc(
	center: Vec2,
	radius_horizontal, radius_vertical: f32,
	origin: Vec2,
	rotation: f32,
	feather_px: f32,
) -> Primitive {
	half_feather := feather_px * 0.5
	padding := half_feather / GLOB.dpi_scaling
	dpi_scale := GLOB.dpi_scaling

	actual_center := center
	sin_angle: f32 = 0
	cos_angle: f32 = 1
	has_rotation := false

	if needs_transform(origin, rotation) {
		rotation_radians := math.to_radians(rotation)
		sin_angle, cos_angle = math.sincos(rotation_radians)
		actual_center = compute_pivot_center(center, origin, sin_angle, cos_angle)
		has_rotation = rotation != 0
	}

	bound_horizontal, bound_vertical := radius_horizontal, radius_vertical
	if has_rotation {
		expanded := rotated_aabb_half_extents(radius_horizontal, radius_vertical, cos_angle, sin_angle)
		bound_horizontal = expanded.x
		bound_vertical = expanded.y
	}

	prim := Primitive {
		bounds      = {
			actual_center.x - bound_horizontal - padding,
			actual_center.y - bound_vertical - padding,
			actual_center.x + bound_horizontal + padding,
			actual_center.y + bound_vertical + padding,
		},
		rotation_sc = has_rotation ? pack_rotation_sc(sin_angle, cos_angle) : 0,
	}
	prim.params.ellipse = Ellipse_Params {
		radii        = {radius_horizontal * dpi_scale, radius_vertical * dpi_scale},
		half_feather = half_feather,
	}
	return prim
}

// Internal
//
// Build an NGon Primitive with bounds, params, and rotation computed from polygon geometry.
// The caller sets color, flags, and uv fields on the returned primitive before submitting.
build_polygon_primitive :: proc(
	center: Vec2,
	sides: int,
	radius: f32,
	origin: Vec2,
	rotation: f32,
	feather_px: f32,
) -> Primitive {
	half_feather := feather_px * 0.5
	padding := half_feather / GLOB.dpi_scaling
	dpi_scale := GLOB.dpi_scaling

	actual_center := center
	if origin != {0, 0} && rotation != 0 {
		sin_a, cos_a := math.sincos(math.to_radians(rotation))
		actual_center = compute_pivot_center(center, origin, sin_a, cos_a)
	}

	rotation_radians := math.to_radians(rotation)
	sin_rot, cos_rot := math.sincos(rotation_radians)

	prim := Primitive {
		bounds      = {
			actual_center.x - radius - padding,
			actual_center.y - radius - padding,
			actual_center.x + radius + padding,
			actual_center.y + radius + padding,
		},
		rotation_sc = rotation != 0 ? pack_rotation_sc(sin_rot, cos_rot) : 0,
	}
	prim.params.ngon = NGon_Params {
		radius       = radius * math.cos(math.PI / f32(sides)) * dpi_scale,
		sides        = f32(sides),
		half_feather = half_feather,
	}
	return prim
}

// Internal
//
// Build a Ring_Arc Primitive with bounds and params computed from ring/arc geometry.
// Pre-computes the angular boundary normals on the CPU so the fragment shader needs
// no per-pixel sin/cos. The radial SDF uses max(inner-r, r-outer) which correctly
// handles pie slices (inner_radius = 0) and full rings.
// The caller sets color, flags, and uv fields on the returned primitive before submitting.
build_ring_arc_primitive :: proc(
	center: Vec2,
	inner_radius, outer_radius: f32,
	start_angle: f32,
	end_angle: f32,
	origin: Vec2,
	rotation: f32,
	feather_px: f32,
) -> (
	Primitive,
	Shape_Flags,
) {
	half_feather := feather_px * 0.5
	padding := half_feather / GLOB.dpi_scaling
	dpi_scale := GLOB.dpi_scaling

	actual_center := center
	rotation_offset: f32 = 0
	if needs_transform(origin, rotation) {
		sin_a, cos_a := math.sincos(math.to_radians(rotation))
		actual_center = compute_pivot_center(center, origin, sin_a, cos_a)
		rotation_offset = math.to_radians(rotation)
	}

	start_rad := math.to_radians(start_angle) + rotation_offset
	end_rad := math.to_radians(end_angle) + rotation_offset

	// Normalize arc span to [0, 2π]
	arc_span := end_rad - start_rad
	if arc_span < 0 {
		arc_span += 2 * math.PI
	}

	// Pre-compute edge normals and arc flags on CPU — no per-pixel trig needed.
	// arc_flags: {} = full ring, {.Arc_Narrow} = span ≤ π (intersect), {.Arc_Wide} = span > π (union)
	arc_flags: Shape_Flags = {}
	normal_start: [2]f32 = {}
	normal_end: [2]f32 = {}

	if arc_span < 2 * math.PI - 0.001 {
		sin_start, cos_start := math.sincos(start_rad)
		sin_end, cos_end := math.sincos(end_rad)
		normal_start = {sin_start, -cos_start}
		normal_end = {-sin_end, cos_end}
		arc_flags = arc_span <= math.PI ? {.Arc_Narrow} : {.Arc_Wide}
	}

	prim := Primitive {
		bounds = {
			actual_center.x - outer_radius - padding,
			actual_center.y - outer_radius - padding,
			actual_center.x + outer_radius + padding,
			actual_center.y + outer_radius + padding,
		},
	}
	prim.params.ring_arc = Ring_Arc_Params {
		inner_radius = inner_radius * dpi_scale,
		outer_radius = outer_radius * dpi_scale,
		normal_start = normal_start,
		normal_end   = normal_end,
		half_feather = half_feather,
	}
	return prim, arc_flags
}

// Apply gradient and outline effects to a primitive. Sets flags, uv.effects, and expands bounds.
// All parameters (outline_width) are in logical pixels, matching the rest of the public API.
// The helper converts to physical pixels for GPU packing internally.
@(private)
apply_shape_effects :: proc(
	prim: ^Primitive,
	kind: Shape_Kind,
	gradient: Gradient,
	outline_color: Color,
	outline_width: f32,
	extra_flags: Shape_Flags = {},
) {
	flags: Shape_Flags = extra_flags
	gradient_dir_sc: u32 = 0

	switch g in gradient {
	case Linear_Gradient:
		flags += {.Gradient}
		prim.uv.effects.gradient_color = g.end_color
		rad := math.to_radians(g.angle)
		sin_a, cos_a := math.sincos(rad)
		gradient_dir_sc = pack_f16_pair(f16(cos_a), f16(sin_a))
	case Radial_Gradient:
		flags += {.Gradient, .Gradient_Radial}
		prim.uv.effects.gradient_color = g.outer_color
	case:
	}

	outline_packed: u32 = 0
	if outline_width > 0 {
		flags += {.Outline}
		prim.uv.effects.outline_color = outline_color
		outline_packed = pack_f16_pair(f16(outline_width * GLOB.dpi_scaling), 0)
		// Expand bounds to contain the outline (bounds are in logical pixels)
		prim.bounds[0] -= outline_width
		prim.bounds[1] -= outline_width
		prim.bounds[2] += outline_width
		prim.bounds[3] += outline_width
	}

	// Set .Rotated flag if rotation_sc was populated by the build proc
	if prim.rotation_sc != 0 {
		flags += {.Rotated}
	}

	prim.uv.effects.gradient_dir_sc = gradient_dir_sc
	prim.uv.effects.outline_packed = outline_packed
	prim.flags = pack_kind_flags(kind, flags)
}

// ---------------------------------------------------------------------------------------------------------------------
// ----- SDF Rectangle procs -----------
// ---------------------------------------------------------------------------------------------------------------------

// Draw a filled rectangle via SDF with optional per-corner rounding radii.
// Use `uniform_radii(rect, roundness)` to compute uniform radii from a 0–1 fraction.
//
// Origin semantics:
//   `origin` is a local offset from the rect's top-left corner that selects both the positioning
//   anchor and the rotation pivot. `rect.x, rect.y` specifies where that anchor point lands in
//   world space. When `origin = {0, 0}` (default), `rect.x, rect.y` is the top-left corner.
//   Rotation always occurs around the anchor point.
rectangle :: proc(
	layer: ^Layer,
	rect: Rectangle,
	color: Color,
	gradient: Gradient = nil,
	outline_color: Color = {},
	outline_width: f32 = 0,
	radii: Rectangle_Radii = {},
	origin: Vec2 = {},
	rotation: f32 = 0,
	feather_px: f32 = DFT_FEATHER_PX,
) {
	prim := build_rrect_primitive(rect, radii, origin, rotation, feather_px)
	prim.color = color
	apply_shape_effects(&prim, .RRect, gradient, outline_color, outline_width)
	prepare_sdf_primitive(layer, prim)
}

// Draw a rectangle with a texture fill via SDF with optional per-corner rounding radii.
// Texture and gradient/outline are mutually exclusive (they share the same storage in the
// primitive). To outline a textured rect, draw the texture first, then a stroke-only rect on top.
// Origin semantics: see `rectangle`.
rectangle_texture :: proc(
	layer: ^Layer,
	rect: Rectangle,
	id: Texture_Id,
	tint: Color = DFT_TINT,
	uv_rect: Rectangle = DFT_UV_RECT,
	sampler: Sampler_Preset = DFT_SAMPLER,
	radii: Rectangle_Radii = {},
	origin: Vec2 = {},
	rotation: f32 = 0,
	feather_px: f32 = DFT_FEATHER_PX,
) {
	prim := build_rrect_primitive(rect, radii, origin, rotation, feather_px)
	prim.color = tint
	tex_flags: Shape_Flags = {.Textured}
	if prim.rotation_sc != 0 {
		tex_flags += {.Rotated}
	}
	prim.flags = pack_kind_flags(.RRect, tex_flags)
	prim.uv.uv_rect = {uv_rect.x, uv_rect.y, uv_rect.width, uv_rect.height}
	prepare_sdf_primitive_textured(layer, prim, id, sampler)
}

// ---------------------------------------------------------------------------------------------------------------------
// ----- SDF Circle procs (emit RRect primitives) ------
// ---------------------------------------------------------------------------------------------------------------------

// Draw a filled circle via SDF (emitted as a fully-rounded RRect).
//
// Origin semantics (Convention B):
//   `origin` is a local offset from the shape's center that selects both the positioning anchor
//   and the rotation pivot. The `center` parameter specifies where that anchor point lands in
//   world space. When `origin = {0, 0}` (default), `center` is the visual center.
//   When `origin = {r, 0}`, the point `r` pixels to the right of the shape center lands at
//   `center`, shifting the shape left by `r`.
circle :: proc(
	layer: ^Layer,
	center: Vec2,
	radius: f32,
	color: Color,
	gradient: Gradient = nil,
	outline_color: Color = {},
	outline_width: f32 = 0,
	origin: Vec2 = {},
	rotation: f32 = 0,
	feather_px: f32 = DFT_FEATHER_PX,
) {
	prim := build_circle_primitive(center, radius, origin, rotation, feather_px)
	prim.color = color
	apply_shape_effects(&prim, .RRect, gradient, outline_color, outline_width)
	prepare_sdf_primitive(layer, prim)
}

// ---------------------------------------------------------------------------------------------------------------------
// ----- SDF Ellipse procs (emit Ellipse primitives) ---
// ---------------------------------------------------------------------------------------------------------------------

// Draw a filled ellipse via SDF.
// Origin semantics: see `circle`.
ellipse :: proc(
	layer: ^Layer,
	center: Vec2,
	radius_horizontal, radius_vertical: f32,
	color: Color,
	gradient: Gradient = nil,
	outline_color: Color = {},
	outline_width: f32 = 0,
	origin: Vec2 = {},
	rotation: f32 = 0,
	feather_px: f32 = DFT_FEATHER_PX,
) {
	prim := build_ellipse_primitive(center, radius_horizontal, radius_vertical, origin, rotation, feather_px)
	prim.color = color
	apply_shape_effects(&prim, .Ellipse, gradient, outline_color, outline_width)
	prepare_sdf_primitive(layer, prim)
}

// ---------------------------------------------------------------------------------------------------------------------
// ----- SDF Polygon procs (emit NGon primitives) ------
// ---------------------------------------------------------------------------------------------------------------------

// Draw a filled regular polygon via SDF.
// `sides` must be >= 3. The polygon is inscribed in a circle of the given `radius`.
// Origin semantics: see `circle`.
polygon :: proc(
	layer: ^Layer,
	center: Vec2,
	sides: int,
	radius: f32,
	color: Color,
	gradient: Gradient = nil,
	outline_color: Color = {},
	outline_width: f32 = 0,
	origin: Vec2 = {},
	rotation: f32 = 0,
	feather_px: f32 = DFT_FEATHER_PX,
) {
	if sides < 3 do return

	prim := build_polygon_primitive(center, sides, radius, origin, rotation, feather_px)
	prim.color = color
	apply_shape_effects(&prim, .NGon, gradient, outline_color, outline_width)
	prepare_sdf_primitive(layer, prim)
}

// ---------------------------------------------------------------------------------------------------------------------
// ----- SDF Ring / Arc procs (emit Ring_Arc primitives) ----
// ---------------------------------------------------------------------------------------------------------------------

// Draw a ring, arc, or pie slice via SDF.
// Full ring by default. Pass start_angle/end_angle (degrees) for partial arcs.
// Use inner_radius = 0 for pie slices (sectors).
// Origin semantics: see `circle`.
ring :: proc(
	layer: ^Layer,
	center: Vec2,
	inner_radius, outer_radius: f32,
	color: Color,
	gradient: Gradient = nil,
	outline_color: Color = {},
	outline_width: f32 = 0,
	start_angle: f32 = 0,
	end_angle: f32 = DFT_CIRC_END_ANGLE,
	origin: Vec2 = {},
	rotation: f32 = 0,
	feather_px: f32 = DFT_FEATHER_PX,
) {
	prim, arc_flags := build_ring_arc_primitive(
		center,
		inner_radius,
		outer_radius,
		start_angle,
		end_angle,
		origin,
		rotation,
		feather_px,
	)
	prim.color = color
	apply_shape_effects(&prim, .Ring_Arc, gradient, outline_color, outline_width, arc_flags)
	prepare_sdf_primitive(layer, prim)
}

// ---------------------------------------------------------------------------------------------------------------------
// ----- SDF Line procs (emit rotated RRect primitives) ----
// ---------------------------------------------------------------------------------------------------------------------

// Draw a line segment via SDF (emitted as a rotated capsule-shaped RRect).
// Round caps are produced by setting corner radii equal to half the thickness.
line :: proc(
	layer: ^Layer,
	start_position, end_position: Vec2,
	color: Color,
	thickness: f32 = DFT_STROKE_THICKNESS,
	outline_color: Color = {},
	outline_width: f32 = 0,
	feather_px: f32 = DFT_FEATHER_PX,
) {
	delta_x := end_position.x - start_position.x
	delta_y := end_position.y - start_position.y
	seg_length := math.sqrt(delta_x * delta_x + delta_y * delta_y)
	if seg_length < 0.0001 do return
	rotation_radians := math.atan2(delta_y, delta_x)
	sin_angle, cos_angle := math.sincos(rotation_radians)

	center_x := (start_position.x + end_position.x) * 0.5
	center_y := (start_position.y + end_position.y) * 0.5

	half_length := seg_length * 0.5
	half_thickness := thickness * 0.5
	cap_radius := half_thickness

	half_feather := feather_px * 0.5
	padding := half_feather / GLOB.dpi_scaling
	dpi_scale := GLOB.dpi_scaling

	// Expand bounds for rotation
	bounds_half := rotated_aabb_half_extents(half_length + cap_radius, half_thickness, cos_angle, sin_angle)

	prim := Primitive {
		bounds      = {
			center_x - bounds_half.x - padding,
			center_y - bounds_half.y - padding,
			center_x + bounds_half.x + padding,
			center_y + bounds_half.y + padding,
		},
		color       = color,
		rotation_sc = pack_rotation_sc(sin_angle, cos_angle),
	}
	prim.params.rrect = RRect_Params {
		half_size    = {(half_length + cap_radius) * dpi_scale, half_thickness * dpi_scale},
		radii        = {
			cap_radius * dpi_scale,
			cap_radius * dpi_scale,
			cap_radius * dpi_scale,
			cap_radius * dpi_scale,
		},
		half_feather = half_feather,
	}
	apply_shape_effects(&prim, .RRect, nil, outline_color, outline_width)
	prepare_sdf_primitive(layer, prim)
}

// Draw a line strip via decomposed SDF line segments.
line_strip :: proc(
	layer: ^Layer,
	points: []Vec2,
	color: Color,
	thickness: f32 = DFT_STROKE_THICKNESS,
	outline_color: Color = {},
	outline_width: f32 = 0,
	feather_px: f32 = DFT_FEATHER_PX,
) {
	if len(points) < 2 do return
	for i in 0 ..< len(points) - 1 {
		line(layer, points[i], points[i + 1], color, thickness, outline_color, outline_width, feather_px)
	}
}


// ---------------------------------------------------------------------------------------------------------------------
// ----- Helpers ----------------
// ---------------------------------------------------------------------------------------------------------------------

// Returns uniform radii (all corners the same) as a fraction of the shorter side.
// `roundness` is clamped to [0, 1]; 0 = sharp corners, 1 = fully rounded (stadium or circle).
uniform_radii :: #force_inline proc(rect: Rectangle, roundness: f32) -> Rectangle_Radii {
	cr := min(rect.width, rect.height) * clamp(roundness, 0, 1) * 0.5
	return {cr, cr, cr, cr}
}

// Return Vec2 pixel offsets for use as the `origin` parameter of draw calls.
// Composable with normal vector +/- arithmetic.
//
// Text anchor helpers are in text.odin (they depend on measure_text / SDL_ttf).

// ----- Rectangle anchors (origin measured from rectangle's top-left) ---------------------------------------------

center_of_rectangle :: #force_inline proc(rectangle: Rectangle) -> Vec2 {
	return {rectangle.width * 0.5, rectangle.height * 0.5}
}

top_left_of_rectangle :: #force_inline proc(rectangle: Rectangle) -> Vec2 {
	return {0, 0}
}

top_of_rectangle :: #force_inline proc(rectangle: Rectangle) -> Vec2 {
	return {rectangle.width * 0.5, 0}
}

top_right_of_rectangle :: #force_inline proc(rectangle: Rectangle) -> Vec2 {
	return {rectangle.width, 0}
}

left_of_rectangle :: #force_inline proc(rectangle: Rectangle) -> Vec2 {
	return {0, rectangle.height * 0.5}
}

right_of_rectangle :: #force_inline proc(rectangle: Rectangle) -> Vec2 {
	return {rectangle.width, rectangle.height * 0.5}
}

bottom_left_of_rectangle :: #force_inline proc(rectangle: Rectangle) -> Vec2 {
	return {0, rectangle.height}
}

bottom_of_rectangle :: #force_inline proc(rectangle: Rectangle) -> Vec2 {
	return {rectangle.width * 0.5, rectangle.height}
}

bottom_right_of_rectangle :: #force_inline proc(rectangle: Rectangle) -> Vec2 {
	return {rectangle.width, rectangle.height}
}

// ----- Triangle anchors (origin measured from AABB top-left) -----------------------------------------------------

center_of_triangle :: #force_inline proc(v1, v2, v3: Vec2) -> Vec2 {
	bounds_min := Vec2{min(v1.x, v2.x, v3.x), min(v1.y, v2.y, v3.y)}
	return (v1 + v2 + v3) / 3 - bounds_min
}

top_left_of_triangle :: #force_inline proc(v1, v2, v3: Vec2) -> Vec2 {
	return {0, 0}
}

top_of_triangle :: #force_inline proc(v1, v2, v3: Vec2) -> Vec2 {
	min_x := min(v1.x, v2.x, v3.x)
	max_x := max(v1.x, v2.x, v3.x)
	return {(max_x - min_x) * 0.5, 0}
}

top_right_of_triangle :: #force_inline proc(v1, v2, v3: Vec2) -> Vec2 {
	min_x := min(v1.x, v2.x, v3.x)
	max_x := max(v1.x, v2.x, v3.x)
	return {max_x - min_x, 0}
}

left_of_triangle :: #force_inline proc(v1, v2, v3: Vec2) -> Vec2 {
	min_y := min(v1.y, v2.y, v3.y)
	max_y := max(v1.y, v2.y, v3.y)
	return {0, (max_y - min_y) * 0.5}
}

right_of_triangle :: #force_inline proc(v1, v2, v3: Vec2) -> Vec2 {
	bounds_min := Vec2{min(v1.x, v2.x, v3.x), min(v1.y, v2.y, v3.y)}
	bounds_max := Vec2{max(v1.x, v2.x, v3.x), max(v1.y, v2.y, v3.y)}
	return {bounds_max.x - bounds_min.x, (bounds_max.y - bounds_min.y) * 0.5}
}

bottom_left_of_triangle :: #force_inline proc(v1, v2, v3: Vec2) -> Vec2 {
	min_y := min(v1.y, v2.y, v3.y)
	max_y := max(v1.y, v2.y, v3.y)
	return {0, max_y - min_y}
}

bottom_of_triangle :: #force_inline proc(v1, v2, v3: Vec2) -> Vec2 {
	bounds_min := Vec2{min(v1.x, v2.x, v3.x), min(v1.y, v2.y, v3.y)}
	bounds_max := Vec2{max(v1.x, v2.x, v3.x), max(v1.y, v2.y, v3.y)}
	return {(bounds_max.x - bounds_min.x) * 0.5, bounds_max.y - bounds_min.y}
}

bottom_right_of_triangle :: #force_inline proc(v1, v2, v3: Vec2) -> Vec2 {
	bounds_min := Vec2{min(v1.x, v2.x, v3.x), min(v1.y, v2.y, v3.y)}
	bounds_max := Vec2{max(v1.x, v2.x, v3.x), max(v1.y, v2.y, v3.y)}
	return bounds_max - bounds_min
}
