#[derive(Debug, Clone, PartialEq)]
pub struct ComponentBbox {
pub min_x: f64,
pub min_y: f64,
pub max_x: f64,
pub max_y: f64,
}
impl ComponentBbox {
pub fn new(min_x: f64, min_y: f64, max_x: f64, max_y: f64) -> Self {
Self {
min_x,
min_y,
max_x,
max_y,
}
}
pub fn width(&self) -> f64 {
self.max_x - self.min_x
}
pub fn height(&self) -> f64 {
self.max_y - self.min_y
}
pub fn area(&self) -> f64 {
self.width() * self.height()
}
pub fn center(&self) -> (f64, f64) {
(
(self.min_x + self.max_x) / 2.0,
(self.min_y + self.max_y) / 2.0,
)
}
pub fn contains(&self, x: f64, y: f64) -> bool {
x >= self.min_x && x <= self.max_x && y >= self.min_y && y <= self.max_y
}
pub fn intersects(&self, other: &Self) -> bool {
self.min_x <= other.max_x
&& self.max_x >= other.min_x
&& self.min_y <= other.max_y
&& self.max_y >= other.min_y
}
pub fn union(&self, other: &Self) -> Self {
Self::new(
self.min_x.min(other.min_x),
self.min_y.min(other.min_y),
self.max_x.max(other.max_x),
self.max_y.max(other.max_y),
)
}
}
#[derive(Debug, Clone, PartialEq)]
#[repr(u8)]
pub enum ComponentDataType {
Uint8 = 0,
Uint16 = 1,
Uint32 = 2,
Int8 = 3,
Int16 = 4,
Int32 = 5,
Float32 = 6,
Float64 = 7,
}
impl ComponentDataType {
pub fn byte_size(&self) -> usize {
match self {
Self::Uint8 | Self::Int8 => 1,
Self::Uint16 | Self::Int16 => 2,
Self::Uint32 | Self::Int32 | Self::Float32 => 4,
Self::Float64 => 8,
}
}
pub fn from_u8(v: u8) -> Option<Self> {
match v {
0 => Some(Self::Uint8),
1 => Some(Self::Uint16),
2 => Some(Self::Uint32),
3 => Some(Self::Int8),
4 => Some(Self::Int16),
5 => Some(Self::Int32),
6 => Some(Self::Float32),
7 => Some(Self::Float64),
_ => None,
}
}
pub fn is_floating_point(&self) -> bool {
matches!(self, Self::Float32 | Self::Float64)
}
pub fn is_integer(&self) -> bool {
!self.is_floating_point()
}
pub fn is_signed(&self) -> bool {
matches!(
self,
Self::Int8 | Self::Int16 | Self::Int32 | Self::Float32 | Self::Float64
)
}
}
#[derive(Debug, Clone)]
pub struct ComponentError {
pub code: u32,
pub message: String,
pub category: ErrorCategory,
}
#[derive(Debug, Clone, PartialEq)]
#[repr(u8)]
pub enum ErrorCategory {
Io = 0,
InvalidInput = 1,
UnsupportedFormat = 2,
OutOfMemory = 3,
Projection = 4,
Internal = 255,
}
impl ComponentError {
pub fn new(code: u32, message: impl Into<String>, category: ErrorCategory) -> Self {
Self {
code,
message: message.into(),
category,
}
}
pub fn invalid_input(msg: impl Into<String>) -> Self {
Self::new(1, msg, ErrorCategory::InvalidInput)
}
pub fn unsupported(msg: impl Into<String>) -> Self {
Self::new(2, msg, ErrorCategory::UnsupportedFormat)
}
pub fn io(msg: impl Into<String>) -> Self {
Self::new(3, msg, ErrorCategory::Io)
}
pub fn out_of_memory(msg: impl Into<String>) -> Self {
Self::new(4, msg, ErrorCategory::OutOfMemory)
}
pub fn projection(msg: impl Into<String>) -> Self {
Self::new(5, msg, ErrorCategory::Projection)
}
pub fn internal(msg: impl Into<String>) -> Self {
Self::new(255, msg, ErrorCategory::Internal)
}
}
impl std::fmt::Display for ComponentError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[{}] {}", self.code, self.message)
}
}
impl std::error::Error for ComponentError {}
pub type ComponentResult<T> = Result<T, ComponentError>;
#[derive(Debug, Clone, PartialEq)]
pub struct PixelCoord {
pub col: u32,
pub row: u32,
}
impl PixelCoord {
pub fn new(col: u32, row: u32) -> Self {
Self { col, row }
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ImageDimensions {
pub width: u32,
pub height: u32,
pub bands: u32,
}
impl ImageDimensions {
pub fn new(width: u32, height: u32, bands: u32) -> Self {
Self {
width,
height,
bands,
}
}
pub fn pixel_count(&self) -> u64 {
self.width as u64 * self.height as u64
}
pub fn band_size_bytes(&self, dtype: &ComponentDataType) -> u64 {
self.pixel_count() * dtype.byte_size() as u64
}
pub fn total_size_bytes(&self, dtype: &ComponentDataType) -> u64 {
self.band_size_bytes(dtype) * self.bands as u64
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bbox_basic() {
let b = ComponentBbox::new(0.0, 0.0, 10.0, 5.0);
assert_eq!(b.width(), 10.0);
assert_eq!(b.height(), 5.0);
assert_eq!(b.area(), 50.0);
assert_eq!(b.center(), (5.0, 2.5));
}
#[test]
fn bbox_contains() {
let b = ComponentBbox::new(0.0, 0.0, 10.0, 10.0);
assert!(b.contains(5.0, 5.0));
assert!(!b.contains(11.0, 5.0));
}
#[test]
fn dtype_byte_sizes() {
assert_eq!(ComponentDataType::Uint8.byte_size(), 1);
assert_eq!(ComponentDataType::Float64.byte_size(), 8);
}
#[test]
fn dtype_from_u8_invalid() {
assert!(ComponentDataType::from_u8(200).is_none());
}
#[test]
fn image_dims_sizes() {
let d = ImageDimensions::new(100, 100, 3);
assert_eq!(d.pixel_count(), 10_000);
assert_eq!(d.total_size_bytes(&ComponentDataType::Float32), 120_000);
}
}