mirror of
https://codeberg.org/waydeer/layer-shika.git
synced 2025-11-17 23:14:23 +00:00
252 lines
5.9 KiB
Rust
252 lines
5.9 KiB
Rust
use crate::errors::DomainError;
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
pub struct LogicalSize {
|
|
width: f32,
|
|
height: f32,
|
|
}
|
|
|
|
impl LogicalSize {
|
|
pub fn new(width: f32, height: f32) -> Result<Self, DomainError> {
|
|
if width <= 0.0 || height <= 0.0 {
|
|
return Err(DomainError::InvalidInput {
|
|
message: format!("Dimensions must be positive, got width={width}, height={height}"),
|
|
});
|
|
}
|
|
if !width.is_finite() || !height.is_finite() {
|
|
return Err(DomainError::InvalidInput {
|
|
message: "Dimensions must be finite values".to_string(),
|
|
});
|
|
}
|
|
Ok(Self { width, height })
|
|
}
|
|
|
|
pub const fn from_raw(width: f32, height: f32) -> Self {
|
|
Self { width, height }
|
|
}
|
|
|
|
pub const fn width(&self) -> f32 {
|
|
self.width
|
|
}
|
|
|
|
pub const fn height(&self) -> f32 {
|
|
self.height
|
|
}
|
|
|
|
pub fn to_physical(&self, scale_factor: ScaleFactor) -> PhysicalSize {
|
|
scale_factor.to_physical(*self)
|
|
}
|
|
|
|
pub fn as_tuple(&self) -> (f32, f32) {
|
|
(self.width, self.height)
|
|
}
|
|
|
|
pub fn clamp_position(
|
|
&self,
|
|
position: LogicalPosition,
|
|
bounds: LogicalSize,
|
|
) -> LogicalPosition {
|
|
let max_x = (bounds.width - self.width).max(0.0);
|
|
let max_y = (bounds.height - self.height).max(0.0);
|
|
|
|
LogicalPosition::new(
|
|
position.x().max(0.0).min(max_x),
|
|
position.y().max(0.0).min(max_y),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl Default for LogicalSize {
|
|
fn default() -> Self {
|
|
Self {
|
|
width: 120.0,
|
|
height: 120.0,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub struct PhysicalSize {
|
|
width: u32,
|
|
height: u32,
|
|
}
|
|
|
|
impl PhysicalSize {
|
|
pub fn new(width: u32, height: u32) -> Result<Self, DomainError> {
|
|
if width == 0 || height == 0 {
|
|
return Err(DomainError::InvalidDimensions { width, height });
|
|
}
|
|
Ok(Self { width, height })
|
|
}
|
|
|
|
pub const fn from_raw(width: u32, height: u32) -> Self {
|
|
Self { width, height }
|
|
}
|
|
|
|
pub const fn width(&self) -> u32 {
|
|
self.width
|
|
}
|
|
|
|
pub const fn height(&self) -> u32 {
|
|
self.height
|
|
}
|
|
|
|
pub fn to_logical(&self, scale_factor: ScaleFactor) -> LogicalSize {
|
|
scale_factor.to_logical(*self)
|
|
}
|
|
|
|
pub fn as_tuple(&self) -> (u32, u32) {
|
|
(self.width, self.height)
|
|
}
|
|
}
|
|
|
|
impl Default for PhysicalSize {
|
|
fn default() -> Self {
|
|
Self {
|
|
width: 120,
|
|
height: 120,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
pub struct ScaleFactor(f32);
|
|
|
|
impl ScaleFactor {
|
|
pub fn new(factor: f32) -> Result<Self, DomainError> {
|
|
if factor <= 0.0 {
|
|
return Err(DomainError::InvalidInput {
|
|
message: format!("Scale factor must be positive, got {factor}"),
|
|
});
|
|
}
|
|
if !factor.is_finite() {
|
|
return Err(DomainError::InvalidInput {
|
|
message: "Scale factor must be a finite value".to_string(),
|
|
});
|
|
}
|
|
Ok(Self(factor))
|
|
}
|
|
|
|
pub const fn from_raw(factor: f32) -> Self {
|
|
Self(factor)
|
|
}
|
|
|
|
#[allow(clippy::cast_precision_loss)]
|
|
pub fn from_120ths(scale_120ths: u32) -> Self {
|
|
Self(scale_120ths as f32 / 120.0)
|
|
}
|
|
|
|
pub const fn value(&self) -> f32 {
|
|
self.0
|
|
}
|
|
|
|
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
|
|
pub fn to_physical(&self, logical: LogicalSize) -> PhysicalSize {
|
|
let width = (logical.width * self.0).round() as u32;
|
|
let height = (logical.height * self.0).round() as u32;
|
|
PhysicalSize::from_raw(width.max(1), height.max(1))
|
|
}
|
|
|
|
#[allow(clippy::cast_precision_loss)]
|
|
pub fn to_logical(&self, physical: PhysicalSize) -> LogicalSize {
|
|
let width = physical.width as f32 / self.0;
|
|
let height = physical.height as f32 / self.0;
|
|
LogicalSize::from_raw(width, height)
|
|
}
|
|
|
|
#[allow(clippy::cast_possible_truncation)]
|
|
pub fn buffer_scale(&self) -> i32 {
|
|
self.0.round() as i32
|
|
}
|
|
|
|
pub fn scale_coordinate(&self, logical_coord: f32) -> f32 {
|
|
logical_coord * self.0
|
|
}
|
|
|
|
pub fn unscale_coordinate(&self, physical_coord: f32) -> f32 {
|
|
physical_coord / self.0
|
|
}
|
|
}
|
|
|
|
impl Default for ScaleFactor {
|
|
fn default() -> Self {
|
|
Self(1.0)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
pub struct LogicalPosition {
|
|
x: f32,
|
|
y: f32,
|
|
}
|
|
|
|
impl LogicalPosition {
|
|
pub fn new(x: f32, y: f32) -> Self {
|
|
Self { x, y }
|
|
}
|
|
|
|
pub const fn x(&self) -> f32 {
|
|
self.x
|
|
}
|
|
|
|
pub const fn y(&self) -> f32 {
|
|
self.y
|
|
}
|
|
|
|
#[allow(clippy::cast_possible_truncation)]
|
|
pub fn to_physical(&self, scale_factor: ScaleFactor) -> PhysicalPosition {
|
|
PhysicalPosition::new(
|
|
(self.x * scale_factor.value()).round() as i32,
|
|
(self.y * scale_factor.value()).round() as i32,
|
|
)
|
|
}
|
|
|
|
pub fn as_tuple(&self) -> (f32, f32) {
|
|
(self.x, self.y)
|
|
}
|
|
}
|
|
|
|
impl Default for LogicalPosition {
|
|
fn default() -> Self {
|
|
Self { x: 0.0, y: 0.0 }
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub struct PhysicalPosition {
|
|
x: i32,
|
|
y: i32,
|
|
}
|
|
|
|
impl PhysicalPosition {
|
|
pub const fn new(x: i32, y: i32) -> Self {
|
|
Self { x, y }
|
|
}
|
|
|
|
pub const fn x(&self) -> i32 {
|
|
self.x
|
|
}
|
|
|
|
pub const fn y(&self) -> i32 {
|
|
self.y
|
|
}
|
|
|
|
#[allow(clippy::cast_precision_loss)]
|
|
pub fn to_logical(&self, scale_factor: ScaleFactor) -> LogicalPosition {
|
|
LogicalPosition::new(
|
|
self.x as f32 / scale_factor.value(),
|
|
self.y as f32 / scale_factor.value(),
|
|
)
|
|
}
|
|
|
|
pub fn as_tuple(&self) -> (i32, i32) {
|
|
(self.x, self.y)
|
|
}
|
|
}
|
|
|
|
#[allow(clippy::derivable_impls)]
|
|
impl Default for PhysicalPosition {
|
|
fn default() -> Self {
|
|
Self { x: 0, y: 0 }
|
|
}
|
|
}
|