// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>

//! Geometry and primitive value types used across the public API.
//!
//! These are the cheap, copy-friendly types that flow through every
//! widget builder, layout method and runtime hook:
//!
//! - [`Color`] — RGBA in `[0.0, 1.0]` floats; `Color::WHITE`,
//!   `Color::BLACK`, `Color::TRANSPARENT` constants and a `Color::hex(r, g, b)`
//!   constructor for byte literals.
//! - [`Rect`] — axis-aligned `(x, y, width, height)`; the universal
//!   layout / hit-test currency.
//! - [`Point`] — a 2D point used by hit testing and gesture progress.
//! - [`Size`] — a `(width, height)` pair without an origin.
//! - [`Corners`] — per-corner radius for the
//!   [`Container`](crate::container()) widget and any other rounded
//!   surface; coerces from `f32` for the uniform case.
//! - [`WidgetId`] — a stable `&'static str` identifier for focus
//!   management, paired with [`crate::App::take_focus_request`].
//!
//! Every type is `Copy` (or `Clone`) so passing them by value is the
//! default. The crate root re-exports them all (`ltk::Color`,
//! `ltk::Rect`, …) so application code rarely needs the `ltk::types::`
//! prefix.

use std::sync::atomic::{ AtomicU32, Ordering };

/// An RGBA color with floating-point channels in the range `[0.0, 1.0]`.
#[ derive( Debug, Clone, Copy, PartialEq ) ]
pub struct Color
{
	/// Red channel `[0.0, 1.0]`.
	pub r: f32,
	/// Green channel `[0.0, 1.0]`.
	pub g: f32,
	/// Blue channel `[0.0, 1.0]`.
	pub b: f32,
	/// Alpha channel — `0.0` is fully transparent, `1.0` is fully opaque.
	pub a: f32,
}

impl Color
{
	/// Fully opaque white.
	pub const WHITE: Self = Self { r: 1., g: 1., b: 1., a: 1. };
	/// Fully opaque black.
	pub const BLACK: Self = Self { r: 0., g: 0., b: 0., a: 1. };
	/// Fully transparent black.
	pub const TRANSPARENT: Self = Self { r: 0., g: 0., b: 0., a: 0. };

	/// Create an opaque color from 8-bit `r`, `g`, `b` components.
	pub const fn hex( r: u8, g: u8, b: u8 ) -> Self
	{
		Self { r: r as f32 / 255.0, g: g as f32 / 255.0, b: b as f32 / 255.0, a: 1.0 }
	}

	/// Create an opaque color from float `r`, `g`, `b` components in `[0.0, 1.0]`.
	pub fn rgb( r: f32, g: f32, b: f32 ) -> Self
	{
		Self { r, g, b, a: 1. }
	}

	/// Create a color from float `r`, `g`, `b`, `a` components in `[0.0, 1.0]`.
	pub fn rgba( r: f32, g: f32, b: f32, a: f32 ) -> Self
	{
		Self { r, g, b, a }
	}

	/// Convert to a [`tiny_skia::Color`] for rendering.
	pub fn to_tiny_skia( self ) -> tiny_skia::Color
	{
		tiny_skia::Color::from_rgba( self.r, self.g, self.b, self.a )
			.unwrap_or( tiny_skia::Color::BLACK )
	}
}

/// A 2-D point in screen coordinates (pixels, top-left origin).
#[ derive( Debug, Clone, Copy, PartialEq, Default ) ]
pub struct Point
{
	/// Horizontal position in pixels.
	pub x: f32,
	/// Vertical position in pixels.
	pub y: f32,
}

/// A width/height pair in pixels.
#[ derive( Debug, Clone, Copy, PartialEq, Default ) ]
pub struct Size
{
	/// Width in pixels.
	pub width: f32,
	/// Height in pixels.
	pub height: f32,
}

/// An axis-aligned rectangle in screen coordinates.
#[ derive( Debug, Clone, Copy, PartialEq, Default ) ]
pub struct Rect
{
	/// Left edge in pixels.
	pub x: f32,
	/// Top edge in pixels.
	pub y: f32,
	/// Width in pixels.
	pub width: f32,
	/// Height in pixels.
	pub height: f32,
}

impl Rect
{
	/// Returns `true` if `p` lies inside or on the boundary of this rect.
	pub fn contains( &self, p: Point ) -> bool
	{
		p.x >= self.x
			&& p.x <= self.x + self.width
			&& p.y >= self.y
			&& p.y <= self.y + self.height
	}

	/// Returns a new rect grown by `amount` pixels on every side.
	pub fn expand( &self, amount: f32 ) -> Self
	{
		Self
		{
			x:      self.x - amount,
			y:      self.y - amount,
			width:  self.width + amount * 2.0,
			height: self.height + amount * 2.0,
		}
	}

	/// Convert to [`tiny_skia::Rect`], returning `None` if dimensions are non-positive.
	pub fn to_tiny_skia( &self ) -> Option<tiny_skia::Rect>
	{
		tiny_skia::Rect::from_xywh( self.x, self.y, self.width, self.height )
	}
}

/// Per-corner radii for a rounded rect, ordered top-left → top-right →
/// bottom-right → bottom-left (clockwise from top-left, matching CSS
/// `border-radius`'s long form). All four values are independent
/// pixel radii — set any subset to `0.0` for a square corner, or use
/// the [`top`](Self::top), [`bottom`](Self::bottom),
/// [`left`](Self::left), [`right`](Self::right) shortcuts for the
/// common asymmetric cases.
///
/// The renderer caps each corner against the inscribed-circle limit
/// `min(width, height) / 2`, mirroring tiny-skia / browser behaviour:
/// passing absurdly large values is a "make this side a pill" idiom
/// rather than an error.
///
/// `f32` and `(f32, f32, f32, f32)` both convert via [`From`] so any
/// API taking `impl Into<Corners>` accepts a uniform radius literal
/// (`.radius( 16.0 )`), an explicit set (`.radius( ( 16.0, 16.0,
/// 0.0, 0.0 ) )`), or a constructed value (`.radius( Corners::top(
/// 16.0 ) )`) interchangeably.
#[ derive( Debug, Clone, Copy, PartialEq, Default ) ]
pub struct Corners
{
	/// Top-left corner radius in pixels.
	pub tl: f32,
	/// Top-right corner radius in pixels.
	pub tr: f32,
	/// Bottom-right corner radius in pixels.
	pub br: f32,
	/// Bottom-left corner radius in pixels.
	pub bl: f32,
}

impl Corners
{
	/// All four corners square (radius `0`).
	pub const ZERO: Self = Self { tl: 0.0, tr: 0.0, br: 0.0, bl: 0.0 };

	/// Uniform radius on every corner — equivalent to `r.into()` and
	/// the most common construction.
	pub const fn all( r: f32 ) -> Self
	{
		Self { tl: r, tr: r, br: r, bl: r }
	}

	/// Rounded top corners, square bottom corners. Matches the CSS
	/// shorthand `border-radius: r r 0 0` and the typical "card sits
	/// flush against the bottom of the screen" pattern (docks,
	/// bottom-anchored modals).
	pub const fn top( r: f32 ) -> Self
	{
		Self { tl: r, tr: r, br: 0.0, bl: 0.0 }
	}

	/// Rounded bottom corners, square top corners. Mirror of
	/// [`top`](Self::top) for top-anchored chrome.
	pub const fn bottom( r: f32 ) -> Self
	{
		Self { tl: 0.0, tr: 0.0, br: r, bl: r }
	}

	/// Rounded left corners, square right corners.
	pub const fn left( r: f32 ) -> Self
	{
		Self { tl: r, tr: 0.0, br: 0.0, bl: r }
	}

	/// Rounded right corners, square left corners.
	pub const fn right( r: f32 ) -> Self
	{
		Self { tl: 0.0, tr: r, br: r, bl: 0.0 }
	}

	/// `true` when every corner is `<= 0` — the renderer can take
	/// the fast straight-rect path.
	pub fn is_zero( &self ) -> bool
	{
		self.tl <= 0.0 && self.tr <= 0.0 && self.br <= 0.0 && self.bl <= 0.0
	}

	/// `true` when every corner has the same radius. Used by the
	/// software path to fall back to the single-radius cubic builder
	/// when the asymmetric path would produce an identical curve.
	pub fn is_uniform( &self ) -> bool
	{
		self.tl == self.tr && self.tr == self.br && self.br == self.bl
	}

	/// The largest of the four radii. Useful for sizing the shader
	/// quad's anti-alias pad — the worst-case AA band has to cover
	/// the steepest curve.
	pub fn max( &self ) -> f32
	{
		self.tl.max( self.tr ).max( self.br ).max( self.bl )
	}

	/// Cap every corner to `min(width, height) / 2`, the inscribed-
	/// circle limit a rounded box can't exceed without degenerating.
	/// Mirrors the clamp the GLES shader applies internally; software
	/// path callers use it before building the path so the cubic
	/// control points stay inside the rect.
	pub fn clamp_to_size( &self, width: f32, height: f32 ) -> Self
	{
		let cap = ( width.min( height ) * 0.5 ).max( 0.0 );
		Self
		{
			tl: self.tl.min( cap ).max( 0.0 ),
			tr: self.tr.min( cap ).max( 0.0 ),
			br: self.br.min( cap ).max( 0.0 ),
			bl: self.bl.min( cap ).max( 0.0 ),
		}
	}

	/// Pack as `[ tl, tr, br, bl ]` for `glUniform4fv`. Order
	/// matches the `vec4 u_radii` convention every fragment shader
	/// in `gles_render::shaders` reads.
	pub fn to_uniform( &self ) -> [ f32; 4 ]
	{
		[ self.tl, self.tr, self.br, self.bl ]
	}
}

impl From<f32> for Corners
{
	fn from( r: f32 ) -> Self { Self::all( r ) }
}

impl From<( f32, f32, f32, f32 )> for Corners
{
	/// Tuple form, ordered `( tl, tr, br, bl )` — matches CSS shorthand.
	fn from( t: ( f32, f32, f32, f32 ) ) -> Self
	{
		Self { tl: t.0, tr: t.1, br: t.2, bl: t.3 }
	}
}

/// A stable widget identifier used for focus management.
///
/// Assign an id to a widget with `.id( WidgetId("my_widget") )`, then request
/// focus via [`App::take_focus_request`](crate::app::App::take_focus_request).
#[ derive( Debug, Clone, Copy, PartialEq, Eq ) ]
pub struct WidgetId( pub &'static str );

/// Pointer cursor shape, sent to the compositor via
/// `wp_cursor_shape_v1` when the pointer enters a widget that
/// declares one. Mirrors `cursor_icon::CursorIcon` 1:1 so the
/// runtime can convert losslessly. Compositors that do not advertise
/// `wp_cursor_shape_v1` ignore these — the user sees their default
/// system cursor.
#[ derive( Debug, Clone, Copy, PartialEq, Eq, Hash ) ]
pub enum CursorShape
{
	Default,
	ContextMenu,
	Help,
	/// "Hand" — clickable buttons, links.
	Pointer,
	/// "Spinning wheel" — work in progress, you can still interact.
	Progress,
	/// "Hourglass" — UI is busy and unresponsive.
	Wait,
	Cell,
	Crosshair,
	/// I-beam — text input fields.
	Text,
	VerticalText,
	Alias,
	Copy,
	Move,
	NoDrop,
	NotAllowed,
	/// Open hand — draggable but not yet dragging.
	Grab,
	/// Closed hand — currently dragging.
	Grabbing,
	EResize,
	NResize,
	NeResize,
	NwResize,
	SResize,
	SeResize,
	SwResize,
	WResize,
	EwResize,
	NsResize,
	NeswResize,
	NwseResize,
	ColResize,
	RowResize,
	AllScroll,
	ZoomIn,
	ZoomOut,
}

impl Default for CursorShape
{
	fn default() -> Self { CursorShape::Default }
}

#[ cfg( test ) ]
mod tests
{
	use super::*;

	// ── Color ─────────────────────────────────────────────────────────────────

	#[ test ]
	fn color_hex_sets_rgb_and_full_alpha()
	{
		let c = Color::hex( 0xFF, 0x00, 0x80 );
		assert!( ( c.r - 1.0 ).abs() < 1e-3 );
		assert!( ( c.g - 0.0 ).abs() < 1e-6 );
		assert!( ( c.b - 0x80 as f32 / 255.0 ).abs() < 1e-3 );
		assert_eq!( c.a, 1.0 );
	}

	#[ test ]
	fn color_rgba_stores_all_channels()
	{
		let c = Color::rgba( 0.1, 0.2, 0.3, 0.4 );
		assert!( ( c.r - 0.1 ).abs() < 1e-6 );
		assert!( ( c.g - 0.2 ).abs() < 1e-6 );
		assert!( ( c.b - 0.3 ).abs() < 1e-6 );
		assert!( ( c.a - 0.4 ).abs() < 1e-6 );
	}

	#[ test ]
	fn color_white_constant_is_all_ones()
	{
		let c = Color::WHITE;
		assert_eq!( c.r, 1. );
		assert_eq!( c.g, 1. );
		assert_eq!( c.b, 1. );
		assert_eq!( c.a, 1. );
	}

	#[ test ]
	fn color_transparent_has_zero_alpha()
	{
		assert_eq!( Color::TRANSPARENT.a, 0. );
	}

	#[ test ]
	fn color_rgb_sets_full_alpha()
	{
		let c = Color::rgb( 0.5, 0.5, 0.5 );
		assert_eq!( c.a, 1.0 );
	}

	// ── Rect ──────────────────────────────────────────────────────────────────

	#[ test ]
	fn rect_contains_interior_point()
	{
		let r = Rect { x: 10., y: 20., width: 100., height: 50. };
		assert!( r.contains( Point { x: 60., y: 45. } ) );
	}

	#[ test ]
	fn rect_contains_boundary_points()
	{
		let r = Rect { x: 0., y: 0., width: 100., height: 100. };
		assert!( r.contains( Point { x: 0., y: 0. } ) );
		assert!( r.contains( Point { x: 100., y: 100. } ) );
	}

	#[ test ]
	fn rect_does_not_contain_exterior_points()
	{
		let r = Rect { x: 10., y: 20., width: 100., height: 50. };
		assert!( !r.contains( Point { x: 5.,   y: 45. } ) );
		assert!( !r.contains( Point { x: 60.,  y: 5.  } ) );
		assert!( !r.contains( Point { x: 200., y: 45. } ) );
		assert!( !r.contains( Point { x: 60.,  y: 80. } ) );
	}

	#[ test ]
	fn rect_expand_grows_in_all_directions()
	{
		let r = Rect { x: 10., y: 10., width: 80., height: 40. };
		let e = r.expand( 5. );
		assert_eq!( e.x,      5. );
		assert_eq!( e.y,      5. );
		assert_eq!( e.width,  90. );
		assert_eq!( e.height, 50. );
	}

	#[ test ]
	fn rect_expand_zero_is_identity()
	{
		let r = Rect { x: 1., y: 2., width: 3., height: 4. };
		let e = r.expand( 0. );
		assert_eq!( r, e );
	}
}

// ─── Length ──────────────────────────────────────────────────────────────────

/// One of the pure relative-or-absolute modes a [`Length`] can carry.
/// Split out so [`Length`] itself can stay `Copy` while still supporting
/// optional clamp bounds — the recursive `Clamp` variant of the original
/// sketch would have forced a `Box` allocation, which on a widget tree
/// that builds these values per frame is the wrong trade.
#[ derive( Debug, Clone, Copy, PartialEq ) ]
pub enum LengthBase
{
	/// Absolute, in logical pixels.
	Px( f32 ),
	/// Percentage of the viewport's width (`Vw(10.0)` == 10 % of width).
	Vw( f32 ),
	/// Percentage of the viewport's height.
	Vh( f32 ),
	/// Percentage of the viewport's **smaller** dimension. The right
	/// default for typography and gutters that must survive a
	/// portrait/landscape rotation without growing absurd.
	Vmin( f32 ),
	/// Percentage of the viewport's **larger** dimension.
	Vmax( f32 ),
	/// Multiple of the root font size (typographic hierarchy: a heading
	/// of `Em(2.0)` is twice the body size, regardless of viewport).
	Em( f32 ),
}

impl LengthBase
{
	fn resolve( &self, viewport: ( f32, f32 ), em_base: f32 ) -> f32
	{
		let ( vw, vh ) = viewport;
		match self
		{
			LengthBase::Px( v )    => *v,
			LengthBase::Vw( pct )  => vw * pct / 100.0,
			LengthBase::Vh( pct )  => vh * pct / 100.0,
			LengthBase::Vmin( pct ) => vw.min( vh ) * pct / 100.0,
			LengthBase::Vmax( pct ) => vw.max( vh ) * pct / 100.0,
			LengthBase::Em( mul )  => em_base * mul,
		}
	}
}

/// A size or distance value that may be expressed in absolute pixels or
/// relative to the rendering surface. Every widget API that used to take
/// `f32` for a size, padding, spacing or font height now takes
/// `impl Into<Length>`, so existing call sites keep compiling unchanged
/// while new code can switch to viewport-relative units for layouts that
/// must scale across screen sizes (portrait phone, landscape tablet,
/// 4K desktop) without per-target tweaks.
///
/// Resolution requires a viewport — passed in as `(width, height)` in
/// **logical** pixels — and an `em_base` (the body-text font size that
/// `Em` is a multiple of). All resolution funnels through
/// [`Length::resolve`], so widgets can stay backend-agnostic.
///
/// Construct directly via the [`LengthBase`] variants
/// (`Length::vmin( 18.0 )`, `Length::px( 24.0 )`, …) or implicitly from
/// `f32`/`i32`/`u32` for the px case so legacy `.size( 24.0 )` style
/// keeps compiling unchanged. Optionally chain `.clamp( min_px, max_px )`
/// to bound a relative value into a safe range.
#[ derive( Debug, Clone, Copy, PartialEq ) ]
pub struct Length
{
	pub base:   LengthBase,
	/// Lower bound in absolute logical px. `None` means unbounded.
	pub min_px: Option<f32>,
	/// Upper bound in absolute logical px. `None` means unbounded.
	pub max_px: Option<f32>,
}

impl Length
{
	/// Default font-size that [`LengthBase::Em`] is a multiple of. Matches
	/// the `typography::BODY` constant of the default theme.
	pub const EM_BASE_DEFAULT: f32 = 16.0;

	pub const fn from_base( base: LengthBase ) -> Self
	{
		Self { base, min_px: None, max_px: None }
	}

	/// Shorthand constructors. `Length::vmin( 18.0 )` reads better than
	/// `Length::from_base( LengthBase::Vmin( 18.0 ) )` at every call site
	/// and the brevity matters when these appear in tight view code.
	pub const fn px(   v: f32 ) -> Self { Self::from_base( LengthBase::Px(   v ) ) }
	pub const fn vw(   v: f32 ) -> Self { Self::from_base( LengthBase::Vw(   v ) ) }
	pub const fn vh(   v: f32 ) -> Self { Self::from_base( LengthBase::Vh(   v ) ) }
	pub const fn vmin( v: f32 ) -> Self { Self::from_base( LengthBase::Vmin( v ) ) }
	pub const fn vmax( v: f32 ) -> Self { Self::from_base( LengthBase::Vmax( v ) ) }
	pub const fn em(   v: f32 ) -> Self { Self::from_base( LengthBase::Em(   v ) ) }

	/// "Design pixel": `px` interpreted at the reference vmin set via
	/// [`set_design_reference`] (defaults to 412 px — the eydos mobile
	/// reference width). The result is a `Vmin` value clamped to
	/// `[px * 0.7, px * 1.5]`, so the layout scales with the screen
	/// without collapsing on tiny surfaces or ballooning on 4K.
	pub fn dp( px: f32 ) -> Self
	{
		let r = design_reference();
		Length::vmin( px / r * 100.0 ).clamp( px * 0.7, px * 1.5 )
	}

	/// Resolve to a concrete logical-pixel value given a viewport and an
	/// `em_base` (the root font size that `Em` is a fraction of).
	pub fn resolve( &self, viewport: ( f32, f32 ), em_base: f32 ) -> f32
	{
		let raw = self.base.resolve( viewport, em_base );
		let lo  = self.min_px;
		let hi  = self.max_px;
		// If both bounds present, normalise their order so swapped args
		// don't produce NaN out of f32::clamp.
		let ( lo, hi ) = match ( lo, hi )
		{
			( Some( a ), Some( b ) ) if a > b => ( Some( b ), Some( a ) ),
			other => other,
		};
		let v = match lo { Some( a ) => raw.max( a ), None => raw };
		match hi { Some( b ) => v.min( b ), None => v }
	}

	/// Cap the resolved value to `[min_px, max_px]`. Bounds are
	/// absolute px because the typical use is "this Vmin should never
	/// shrink past readable nor balloon past comfortable"; bounding
	/// a relative value with another relative value is rare enough to
	/// not justify boxing the type. If you swap min/max the resolver
	/// tolerates it instead of panicking.
	pub fn clamp( mut self, min_px: f32, max_px: f32 ) -> Length
	{
		self.min_px = Some( min_px );
		self.max_px = Some( max_px );
		self
	}

	/// One-sided bound: never resolve below `min_px`. Named `at_least`
	/// (rather than `min`) to avoid clashing visually with `f32::min`,
	/// which has the opposite semantics ("return the smaller of two").
	pub fn at_least( mut self, min_px: f32 ) -> Length
	{
		self.min_px = Some( min_px );
		self
	}

	/// One-sided bound: never resolve above `max_px`. Counterpart to
	/// [`Self::at_least`].
	pub fn at_most( mut self, max_px: f32 ) -> Length
	{
		self.max_px = Some( max_px );
		self
	}
}

static DESIGN_REFERENCE_BITS: AtomicU32 = AtomicU32::new( 412.0_f32.to_bits() );

/// Set the reference vmin width that [`Length::dp`] interprets `px` against.
/// Call once at startup (e.g. before [`crate::run`]) to align the design
/// scale to the surface mock-up the app was designed for.
pub fn set_design_reference( reference_vmin: f32 )
{
	DESIGN_REFERENCE_BITS.store( reference_vmin.to_bits(), Ordering::Relaxed );
}

/// Current value used by [`Length::dp`] — the px width at which `dp(n)`
/// resolves to `n` logical pixels.
pub fn design_reference() -> f32
{
	f32::from_bits( DESIGN_REFERENCE_BITS.load( Ordering::Relaxed ) )
}

impl From<f32> for Length
{
	fn from( v: f32 ) -> Self { Length::px( v ) }
}

impl From<i32> for Length
{
	fn from( v: i32 ) -> Self { Length::px( v as f32 ) }
}

impl From<u32> for Length
{
	fn from( v: u32 ) -> Self { Length::px( v as f32 ) }
}

impl From<LengthBase> for Length
{
	fn from( base: LengthBase ) -> Self { Length::from_base( base ) }
}

#[ cfg( test ) ]
mod length_tests
{
	use super::Length;

	#[ test ]
	fn px_is_passthrough()
	{
		assert_eq!( Length::px( 42.0 ).resolve( ( 800.0, 600.0 ), 16.0 ), 42.0 );
	}

	#[ test ]
	fn vw_vh_are_percent_of_viewport()
	{
		assert_eq!( Length::vw( 50.0 ).resolve( ( 800.0, 600.0 ), 16.0 ), 400.0 );
		assert_eq!( Length::vh( 25.0 ).resolve( ( 800.0, 600.0 ), 16.0 ), 150.0 );
	}

	#[ test ]
	fn vmin_picks_smaller_side()
	{
		assert_eq!( Length::vmin( 10.0 ).resolve( ( 800.0, 600.0 ), 16.0 ), 60.0 );
		assert_eq!( Length::vmin( 10.0 ).resolve( ( 600.0, 800.0 ), 16.0 ), 60.0 );
	}

	#[ test ]
	fn vmax_picks_larger_side()
	{
		assert_eq!( Length::vmax( 10.0 ).resolve( ( 800.0, 600.0 ), 16.0 ), 80.0 );
	}

	#[ test ]
	fn em_uses_em_base()
	{
		assert_eq!( Length::em( 2.0 ).resolve( ( 800.0, 600.0 ), 18.0 ), 36.0 );
	}

	#[ test ]
	fn clamp_bounds_relative_value()
	{
		// 50 % of the smaller side (= 300) capped to [100, 200] → 200.
		let l = Length::vmin( 50.0 ).clamp( 100.0, 200.0 );
		assert_eq!( l.resolve( ( 800.0, 600.0 ), 16.0 ), 200.0 );

		// 1 % of the smaller side (= 6) lifted to the min of 50.
		let l2 = Length::vmin( 1.0 ).clamp( 50.0, 200.0 );
		assert_eq!( l2.resolve( ( 800.0, 600.0 ), 16.0 ), 50.0 );

		// Caller swapped min/max — resolver tolerates without panic.
		let l3 = Length::vmin( 50.0 ).clamp( 200.0, 100.0 );
		assert_eq!( l3.resolve( ( 800.0, 600.0 ), 16.0 ), 200.0 );
	}

	#[ test ]
	fn f32_converts_to_px()
	{
		let l: Length = 24.0_f32.into();
		assert_eq!( l.base, super::LengthBase::Px( 24.0 ) );
	}
}
