microcad-lang 0.5.0

µcad language
Documentation
// Copyright © 2025-2026 The µcad authors <info@microcad.xyz>
// SPDX-License-Identifier: AGPL-3.0-or-later

//! Matrix value type

pub mod error;
pub mod ops;

use crate::ty::*;
use microcad_core::*;

pub use error::*;

const OUTPUT_PRECISION: i32 = 14;

/// A numeric value
#[derive(Clone, Debug)]
pub struct Quantity {
    /// The numeric value of the quantity.
    pub value: Scalar,
    /// The quantity type with a base unit.
    pub quantity_type: QuantityType,
    /// The original unit of the quantity,
    pub unit: Unit,
}

impl PartialEq for Quantity {
    fn eq(&self, other: &Self) -> bool {
        // 1. Ensure the types match first
        if self.quantity_type != other.quantity_type {
            return false;
        }

        // 2. Compare values within the allowed precision
        let epsilon = 10.0_f64.powi(-OUTPUT_PRECISION);
        (self.value - other.value).abs() < epsilon
    }
}

impl Quantity {
    /// Create a new quantity.
    pub fn new(value: Scalar, quantity_type: QuantityType) -> Self {
        Self {
            value,
            unit: quantity_type.base_unit(),
            quantity_type,
        }
    }
    /// Transforms the internal value using a closure.
    pub fn map<F>(self, f: F) -> Self
    where
        F: FnOnce(Scalar) -> Scalar,
    {
        Self {
            value: f(self.value),
            quantity_type: self.quantity_type,
            unit: self.unit,
        }
    }

    /// Create a new Scalar quantity.
    pub fn scalar(value: Scalar) -> Self {
        Quantity::new(value, QuantityType::Scalar)
    }

    /// Create a new Length quantity in millimeters.
    pub fn length(length: Scalar) -> Self {
        Quantity::new(length, QuantityType::Length)
    }

    /// Calculate the power of quantity.
    ///
    /// *Note: This function has not been implemented completely.*
    pub fn pow(&self, rhs: &Quantity) -> Self {
        match (&self.quantity_type, &rhs.quantity_type) {
            (QuantityType::Scalar, QuantityType::Scalar) => {
                Quantity::new(self.value.powf(rhs.value), QuantityType::Scalar)
            }
            _ => todo!(),
        }
    }

    /// Calculate the power of quantity and an integer.
    ///
    /// *Note: This function has not been implemented completely.*
    pub fn pow_int(&self, rhs: &Integer) -> Self {
        match &self.quantity_type {
            QuantityType::Scalar => {
                Quantity::new(self.value.powi(*rhs as i32), QuantityType::Scalar)
            }
            QuantityType::Length => todo!(),
            QuantityType::Area => todo!(),
            QuantityType::Volume => todo!(),
            QuantityType::Density => todo!(),
            QuantityType::Angle => todo!(),
            QuantityType::Weight => todo!(),
            QuantityType::Invalid => todo!(),
        }
    }
}

impl PartialOrd for Quantity {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        if self.quantity_type == other.quantity_type {
            self.value.partial_cmp(&other.value)
        } else {
            None
        }
    }
}

impl From<Scalar> for Quantity {
    fn from(value: Scalar) -> Self {
        Self::new(value, QuantityType::Scalar)
    }
}

impl From<Integer> for Quantity {
    fn from(value: Integer) -> Self {
        Self::new(value as Scalar, QuantityType::Scalar)
    }
}

impl From<Length> for Quantity {
    fn from(length: Length) -> Self {
        Self::new(*length, QuantityType::Length)
    }
}

impl Ty for Quantity {
    fn ty(&self) -> Type {
        Type::Quantity(self.quantity_type.clone())
    }
}

impl std::fmt::Display for Quantity {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{}{}",
            round::round(self.unit.denormalize(self.value), OUTPUT_PRECISION),
            self.unit
        )
    }
}

impl std::hash::Hash for Quantity {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        bytemuck::bytes_of(&self.value).hash(state);
        self.quantity_type.hash(state)
    }
}