use std::{borrow::Cow, f64::consts::PI, sync::Arc};
use stem_material::prelude::*;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "serde")]
use serde_mosaic::serialize_arc_link;
use compare_variables::compare_variables;
use planar_geo::prelude::*;
use super::magnet::Magnet;
#[doc = ""]
#[cfg_attr(
feature = "doc-images",
doc = "![Block magnet definitions][drawing_block_magnet]"
)]
#[cfg_attr(
feature = "doc-images",
embed_doc_image::embed_doc_image("drawing_block_magnet", "docs/img/drawing_block_magnet.svg")
)]
#[cfg_attr(
not(feature = "doc-images"),
doc = "**Doc images not enabled**. Compile docs with
`cargo doc --features 'doc-images'` and Rust version >= 1.54."
)]
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
pub struct BlockMagnet {
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_quantity"))]
length: Length, #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_quantity"))]
width: Length, #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_quantity"))]
thickness: Length, #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_quantity"))]
fillet: Length, #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_arc_link"))]
material: Arc<Material>,
#[cfg_attr(feature = "serde", serde(skip))]
shape: Shape,
#[cfg_attr(feature = "serde", serde(skip))]
north_shape: Shape,
#[cfg_attr(feature = "serde", serde(skip))]
south_shape: Shape,
}
impl BlockMagnet {
pub fn new(
length: Length,
width: Length,
thickness: Length,
fillet: Length,
material: Arc<Material>,
) -> Result<Self, crate::error::Error> {
let zero = Length::new::<meter>(0.0);
compare_variables!(val zero <= fillet)?;
compare_variables!(val zero < length)?;
compare_variables!(val zero < width)?;
compare_variables!(val zero < thickness)?;
let halfed_magnet_thickness = thickness / 2.0;
compare_variables!(halfed_magnet_thickness >= fillet)?;
let halfed_magnet_width = width / 2.0;
compare_variables!(halfed_magnet_width >= fillet)?;
let xmin = -0.5 * width.get::<meter>();
let xmax = -xmin;
let ymin = 0.0;
let ymax = thickness.get::<meter>();
let e = DEFAULT_EPSILON;
let m = DEFAULT_MAX_ULPS;
let f = fillet.get::<meter>();
let (shape, north_shape, south_shape) = if fillet == zero {
let polysegment =
Polysegment::from_points(&[[xmin, ymin], [xmax, ymin], [xmax, ymax], [xmin, ymax]]);
let shape = Shape::new(vec![polysegment.into()])?;
let polysegment = Polysegment::from_points(&[
[xmin, ymin],
[xmax, ymin],
[xmax, 0.5 * ymax],
[xmin, 0.5 * ymax],
]);
let north_shape = Shape::new(vec![polysegment.into()])?;
let polysegment = Polysegment::from_points(&[
[xmin, 0.5 * ymax],
[xmax, 0.5 * ymax],
[xmax, ymax],
[xmin, ymax],
]);
let south_shape = Shape::new(vec![polysegment.into()])?;
(shape, north_shape, south_shape)
} else {
let mut polysegment = Polysegment::new();
let a_lr = ArcSegment::fillet([xmax, ymax], [xmax, ymin], [xmin, ymin], f, e, m)?;
polysegment.push_back(a_lr.clone().into());
let a_ll = ArcSegment::fillet([xmax, ymin], [xmin, ymin], [xmin, ymax], f, e, m)?;
polysegment.push_back(a_ll.clone().into());
let a_ul = ArcSegment::fillet([xmin, ymin], [xmin, ymax], [xmax, ymax], f, e, m)?;
polysegment.push_back(a_ul.clone().into());
let a_ur = ArcSegment::fillet([xmin, ymax], [xmax, ymax], [xmax, ymin], f, e, m)?;
polysegment.push_back(a_ur.clone().into());
let shape = Shape::new(vec![polysegment.into()])?;
let mut s = Polysegment::new();
s.push_back(a_lr.into());
s.push_back(a_ll.into());
s.extend_back([xmin, 0.5 * ymax]);
s.extend_back([xmax, 0.5 * ymax]);
let north_shape = Shape::new(vec![s.into()])?;
let mut n = Polysegment::new();
n.push_back(a_ul.into());
n.push_back(a_ur.into());
n.extend_back([xmax, 0.5 * ymax]);
n.extend_back([xmin, 0.5 * ymax]);
let south_shape = Shape::new(vec![n.into()])?;
(shape, north_shape, south_shape)
};
return Ok(BlockMagnet {
length,
width,
thickness,
fillet,
material,
shape,
north_shape,
south_shape,
});
}
pub fn fillet(&self) -> Length {
return self.fillet;
}
}
impl Default for BlockMagnet {
fn default() -> Self {
Self::new(
Length::new::<millimeter>(10.0),
Length::new::<millimeter>(10.0),
Length::new::<millimeter>(10.0),
Length::new::<millimeter>(0.0),
Arc::new(Material::default()),
)
.expect("valid inputs")
}
}
#[cfg_attr(feature = "serde", typetag::serde)]
impl Magnet for BlockMagnet {
fn width(&self) -> Length {
return self.width;
}
fn length(&self) -> Length {
return self.length;
}
fn thickness(&self) -> Length {
return self.thickness;
}
fn material(&self) -> &Material {
return &*self.material;
}
fn material_arc(&self) -> Arc<Material> {
return self.material.clone();
}
fn shape(&self) -> Cow<'_, Shape> {
return Cow::Borrowed(&self.shape);
}
fn north_south_shapes(&self) -> [Cow<'_, Shape>; 2] {
return [
Cow::Borrowed(&self.north_shape),
Cow::Borrowed(&self.south_shape),
];
}
fn area(&self) -> Area {
use uom::typenum::P2;
return self.thickness * self.width + (PI - 4.0) * self.fillet.powi(P2::new());
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for BlockMagnet {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde_mosaic::deserialize_arc_link;
use stem_material::prelude::deserialize_quantity;
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct BlockMagnetSerde {
#[serde(deserialize_with = "deserialize_quantity")]
length: Length,
#[serde(deserialize_with = "deserialize_quantity")]
width: Length,
#[serde(deserialize_with = "deserialize_quantity")]
thickness: Length,
#[serde(deserialize_with = "deserialize_quantity")]
fillet: Length,
#[serde(deserialize_with = "deserialize_arc_link")]
material: Arc<Material>,
}
let m = BlockMagnetSerde::deserialize(deserializer)?;
return BlockMagnet::new(m.length, m.width, m.thickness, m.fillet, m.material)
.map_err(serde::de::Error::custom);
}
}