use crate::types::Handle;
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Scale {
pub handle: Handle,
pub owner_handle: Handle,
pub name: String,
pub paper_units: f64,
pub drawing_units: f64,
pub is_unit_scale: bool,
pub is_temporary: bool,
}
impl Scale {
pub const OBJECT_NAME: &'static str = "SCALE";
pub const SUBCLASS_MARKER: &'static str = "AcDbScale";
pub fn new(name: &str, paper_units: f64, drawing_units: f64) -> Self {
let is_unit_scale = (paper_units - drawing_units).abs() < 1e-10;
Scale {
handle: Handle::NULL,
owner_handle: Handle::NULL,
name: name.to_string(),
paper_units,
drawing_units,
is_unit_scale,
is_temporary: false,
}
}
pub fn unit_scale() -> Self {
Scale {
handle: Handle::NULL,
owner_handle: Handle::NULL,
name: "1:1".to_string(),
paper_units: 1.0,
drawing_units: 1.0,
is_unit_scale: true,
is_temporary: false,
}
}
pub fn from_ratio(name: &str, paper: i32, drawing: i32) -> Self {
Self::new(name, paper as f64, drawing as f64)
}
pub fn factor(&self) -> f64 {
if self.drawing_units.abs() < 1e-10 {
1.0
} else {
self.paper_units / self.drawing_units
}
}
pub fn inverse_factor(&self) -> f64 {
if self.paper_units.abs() < 1e-10 {
1.0
} else {
self.drawing_units / self.paper_units
}
}
pub fn is_reduction(&self) -> bool {
self.factor() < 1.0 - 1e-10
}
pub fn is_enlargement(&self) -> bool {
self.factor() > 1.0 + 1e-10
}
pub fn ratio_string(&self) -> String {
if self.paper_units >= self.drawing_units {
let ratio = (self.paper_units / self.drawing_units).round() as i64;
format!("{}:1", ratio)
} else {
let ratio = (self.drawing_units / self.paper_units).round() as i64;
format!("1:{}", ratio)
}
}
pub fn scale_1_1() -> Self {
Self::new("1:1", 1.0, 1.0)
}
pub fn scale_1_2() -> Self {
Self::new("1:2", 1.0, 2.0)
}
pub fn scale_1_4() -> Self {
Self::new("1:4", 1.0, 4.0)
}
pub fn scale_1_5() -> Self {
Self::new("1:5", 1.0, 5.0)
}
pub fn scale_1_8() -> Self {
Self::new("1:8", 1.0, 8.0)
}
pub fn scale_1_10() -> Self {
Self::new("1:10", 1.0, 10.0)
}
pub fn scale_1_16() -> Self {
Self::new("1:16", 1.0, 16.0)
}
pub fn scale_1_20() -> Self {
Self::new("1:20", 1.0, 20.0)
}
pub fn scale_1_30() -> Self {
Self::new("1:30", 1.0, 30.0)
}
pub fn scale_1_40() -> Self {
Self::new("1:40", 1.0, 40.0)
}
pub fn scale_1_50() -> Self {
Self::new("1:50", 1.0, 50.0)
}
pub fn scale_1_100() -> Self {
Self::new("1:100", 1.0, 100.0)
}
pub fn scale_1_128() -> Self {
Self::new("1:128", 1.0, 128.0)
}
pub fn scale_2_1() -> Self {
Self::new("2:1", 2.0, 1.0)
}
pub fn scale_4_1() -> Self {
Self::new("4:1", 4.0, 1.0)
}
pub fn scale_8_1() -> Self {
Self::new("8:1", 8.0, 1.0)
}
pub fn scale_10_1() -> Self {
Self::new("10:1", 10.0, 1.0)
}
pub fn scale_100_1() -> Self {
Self::new("100:1", 100.0, 1.0)
}
pub fn scale_1in_1ft() -> Self {
Self::new("1\" = 1'", 1.0, 12.0)
}
pub fn scale_half_in_1ft() -> Self {
Self::new("1/2\" = 1'", 0.5, 12.0)
}
pub fn scale_quarter_in_1ft() -> Self {
Self::new("1/4\" = 1'", 0.25, 12.0)
}
pub fn scale_eighth_in_1ft() -> Self {
Self::new("1/8\" = 1'", 0.125, 12.0)
}
pub fn scale_3_4in_1ft() -> Self {
Self::new("3/4\" = 1'", 0.75, 12.0)
}
pub fn scale_3_8in_1ft() -> Self {
Self::new("3/8\" = 1'", 0.375, 12.0)
}
pub fn scale_3_16in_1ft() -> Self {
Self::new("3/16\" = 1'", 0.1875, 12.0)
}
pub fn scale_3_32in_1ft() -> Self {
Self::new("3/32\" = 1'", 0.09375, 12.0)
}
pub fn scale_1_5in_1ft() -> Self {
Self::new("1 1/2\" = 1'", 1.5, 12.0)
}
pub fn scale_3in_1ft() -> Self {
Self::new("3\" = 1'", 3.0, 12.0)
}
pub fn scale_6in_1ft() -> Self {
Self::new("6\" = 1'", 6.0, 12.0)
}
pub fn standard_metric_scales() -> Vec<Scale> {
vec![
Self::scale_1_1(),
Self::scale_1_2(),
Self::scale_1_4(),
Self::scale_1_5(),
Self::scale_1_8(),
Self::scale_1_10(),
Self::scale_1_16(),
Self::scale_1_20(),
Self::scale_1_30(),
Self::scale_1_40(),
Self::scale_1_50(),
Self::scale_1_100(),
Self::scale_1_128(),
Self::scale_2_1(),
Self::scale_4_1(),
Self::scale_8_1(),
Self::scale_10_1(),
Self::scale_100_1(),
]
}
pub fn standard_imperial_scales() -> Vec<Scale> {
vec![
Self::scale_1_1(),
Self::scale_6in_1ft(),
Self::scale_3in_1ft(),
Self::scale_1_5in_1ft(),
Self::scale_1in_1ft(),
Self::scale_3_4in_1ft(),
Self::scale_half_in_1ft(),
Self::scale_3_8in_1ft(),
Self::scale_quarter_in_1ft(),
Self::scale_3_16in_1ft(),
Self::scale_eighth_in_1ft(),
Self::scale_3_32in_1ft(),
]
}
}
impl Default for Scale {
fn default() -> Self {
Self::unit_scale()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_scale_creation() {
let scale = Scale::new("1:50", 1.0, 50.0);
assert_eq!(scale.name, "1:50");
assert_eq!(scale.paper_units, 1.0);
assert_eq!(scale.drawing_units, 50.0);
assert!(!scale.is_unit_scale);
}
#[test]
fn test_unit_scale() {
let scale = Scale::unit_scale();
assert_eq!(scale.name, "1:1");
assert!(scale.is_unit_scale);
assert!((scale.factor() - 1.0).abs() < 1e-10);
}
#[test]
fn test_from_ratio() {
let scale = Scale::from_ratio("1:25", 1, 25);
assert_eq!(scale.paper_units, 1.0);
assert_eq!(scale.drawing_units, 25.0);
}
#[test]
fn test_factor() {
let scale_1_50 = Scale::new("1:50", 1.0, 50.0);
assert!((scale_1_50.factor() - 0.02).abs() < 1e-10);
let scale_2_1 = Scale::new("2:1", 2.0, 1.0);
assert!((scale_2_1.factor() - 2.0).abs() < 1e-10);
let scale_1_1 = Scale::unit_scale();
assert!((scale_1_1.factor() - 1.0).abs() < 1e-10);
}
#[test]
fn test_inverse_factor() {
let scale_1_50 = Scale::new("1:50", 1.0, 50.0);
assert!((scale_1_50.inverse_factor() - 50.0).abs() < 1e-10);
let scale_2_1 = Scale::new("2:1", 2.0, 1.0);
assert!((scale_2_1.inverse_factor() - 0.5).abs() < 1e-10);
}
#[test]
fn test_is_reduction() {
let scale = Scale::new("1:50", 1.0, 50.0);
assert!(scale.is_reduction());
assert!(!scale.is_enlargement());
}
#[test]
fn test_is_enlargement() {
let scale = Scale::new("5:1", 5.0, 1.0);
assert!(scale.is_enlargement());
assert!(!scale.is_reduction());
}
#[test]
fn test_unit_is_neither() {
let scale = Scale::unit_scale();
assert!(!scale.is_reduction());
assert!(!scale.is_enlargement());
}
#[test]
fn test_ratio_string() {
let scale_1_50 = Scale::new("1:50", 1.0, 50.0);
assert_eq!(scale_1_50.ratio_string(), "1:50");
let scale_2_1 = Scale::new("2:1", 2.0, 1.0);
assert_eq!(scale_2_1.ratio_string(), "2:1");
let scale_1_1 = Scale::unit_scale();
assert_eq!(scale_1_1.ratio_string(), "1:1");
}
#[test]
fn test_zero_drawing_units_factor() {
let scale = Scale::new("Invalid", 1.0, 0.0);
assert!((scale.factor() - 1.0).abs() < 1e-10);
}
#[test]
fn test_zero_paper_units_inverse() {
let scale = Scale::new("Invalid", 0.0, 1.0);
assert!((scale.inverse_factor() - 1.0).abs() < 1e-10);
}
#[test]
fn test_standard_metric_scales() {
let scales = Scale::standard_metric_scales();
assert!(scales.len() >= 15);
assert!(scales.iter().any(|s| s.name == "1:1"));
assert!(scales.iter().any(|s| s.name == "1:50"));
}
#[test]
fn test_standard_imperial_scales() {
let scales = Scale::standard_imperial_scales();
assert!(scales.len() >= 10);
assert!(scales.iter().any(|s| s.name == "1/4\" = 1'"));
}
#[test]
fn test_scale_1_1() {
let scale = Scale::scale_1_1();
assert!(scale.is_unit_scale || (scale.factor() - 1.0).abs() < 1e-10);
}
#[test]
fn test_scale_1_50() {
let scale = Scale::scale_1_50();
assert!((scale.factor() - 0.02).abs() < 1e-10);
}
#[test]
fn test_scale_1_100() {
let scale = Scale::scale_1_100();
assert!((scale.factor() - 0.01).abs() < 1e-10);
}
#[test]
fn test_scale_2_1() {
let scale = Scale::scale_2_1();
assert!((scale.factor() - 2.0).abs() < 1e-10);
}
#[test]
fn test_imperial_scale_1in_1ft() {
let scale = Scale::scale_1in_1ft();
assert!((scale.factor() - (1.0 / 12.0)).abs() < 1e-10);
}
#[test]
fn test_imperial_scale_quarter_in() {
let scale = Scale::scale_quarter_in_1ft();
assert!((scale.factor() - (0.25 / 12.0)).abs() < 1e-10);
}
#[test]
fn test_default() {
let scale = Scale::default();
assert_eq!(scale.name, "1:1");
assert!(scale.is_unit_scale);
}
#[test]
fn test_temporary_flag() {
let mut scale = Scale::new("Temp", 1.0, 5.0);
assert!(!scale.is_temporary);
scale.is_temporary = true;
assert!(scale.is_temporary);
}
#[test]
fn test_clone() {
let scale = Scale::new("1:25", 1.0, 25.0);
let cloned = scale.clone();
assert_eq!(scale.name, cloned.name);
assert_eq!(scale.paper_units, cloned.paper_units);
assert_eq!(scale.drawing_units, cloned.drawing_units);
}
}