use std::{borrow::Cow, f64::consts::FRAC_PI_2, 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::*;
use crate::magnet::Magnet;
#[doc = ""]
#[cfg_attr(
feature = "doc-images",
doc = "![Arc segment magnet definitions][drawing_arc_segment_magnet]"
)]
#[cfg_attr(
feature = "doc-images",
embed_doc_image::embed_doc_image(
"drawing_arc_segment_magnet",
"docs/img/drawing_arc_segment_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."
)]
#[doc = ""]
#[cfg_attr(
feature = "doc-images",
doc = "![Magnet form dependence on radius][drawing_arc_segment_magnet_pos_neg_radii]"
)]
#[cfg_attr(
feature = "doc-images",
embed_doc_image::embed_doc_image(
"drawing_arc_segment_magnet_pos_neg_radii",
"docs/img/drawing_arc_segment_magnet_pos_neg_radii.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."
)]
#[doc = ""]
#[cfg_attr(
feature = "doc-images",
doc = "![Different arc segment shapes][arc_segment_vary_air_gap_radius]"
)]
#[cfg_attr(
feature = "doc-images",
embed_doc_image::embed_doc_image(
"arc_segment_vary_air_gap_radius",
"docs/img/arc_segment_vary_air_gap_radius.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 ArcSegmentMagnet {
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_quantity"))]
length: Length,
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_quantity"))]
side_thickness: Length,
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_quantity"))]
core_radius: Length,
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_quantity"))]
air_gap_radius: Length,
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_angle"))]
angle: f64,
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_arc_link",))]
material: Arc<Material>,
#[cfg_attr(feature = "serde", serde(skip))]
center_thickness: Length,
#[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 ArcSegmentMagnet {
pub fn new(
length: Length,
core_radius: Length,
air_gap_radius: Length,
side_thickness: Length,
angle: f64,
material: Arc<Material>,
) -> Result<Self, crate::error::Error> {
let zero = Length::new::<meter>(0.0);
compare_variables!(val zero < length)?;
let mut offset = [side_thickness.get::<meter>(), 0.0];
offset.rotate([0.0, 0.0], FRAC_PI_2 - 0.5 * angle);
let [core_arc, air_gap_arc] =
core_and_air_gap_from_air_gap_arc_radius(core_radius, air_gap_radius, offset, angle)?;
let center_thickness =
Length::new::<meter>(air_gap_arc.segment_point(0.5)[1]) - core_radius;
let [shape, north_shape, south_shape] = shapes(
core_arc,
air_gap_arc,
offset,
core_radius.is_sign_positive(),
)?;
return Ok(Self {
length,
core_radius,
air_gap_radius,
angle,
side_thickness,
material,
shape,
center_thickness,
north_shape,
south_shape,
});
}
pub fn with_const_thickness(
length: Length,
core_radius: Length,
side_thickness: Length,
angle: f64,
material: Arc<Material>,
) -> Result<Self, crate::error::Error> {
let air_gap_radius = core_radius + side_thickness;
return Self::new(
length,
core_radius,
air_gap_radius,
side_thickness,
angle,
material,
);
}
#[doc = ""]
#[cfg_attr(
feature = "doc-images",
doc = "![Different magnet shapes from center thickness][arc_segment_vary_center_thickness]"
)]
#[cfg_attr(
feature = "doc-images",
embed_doc_image::embed_doc_image(
"arc_segment_vary_center_thickness",
"docs/img/arc_segment_vary_center_thickness.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."
)]
pub fn with_center_thickness(
length: Length,
core_radius: Length,
side_thickness: Length,
center_thickness: Length,
angle: f64,
material: Arc<Material>,
) -> Result<Self, crate::error::Error> {
let zero = Length::new::<meter>(0.0);
compare_variables!(val zero < center_thickness)?;
let mut offset = [side_thickness.get::<meter>(), 0.0];
offset.rotate([0.0, 0.0], FRAC_PI_2 - 0.5 * angle);
let [core_arc, air_gap_arc] = core_and_air_gap_arc_from_center_thickness(
core_radius,
center_thickness,
offset,
angle,
)?;
let sign = if air_gap_arc.is_positive() {
if core_radius.is_sign_positive() {
-1.0
} else {
1.0
}
} else {
if core_radius.is_sign_positive() {
1.0
} else {
-1.0
}
};
let air_gap_radius = Length::new::<meter>(air_gap_arc.radius() * sign);
let [shape, north_shape, south_shape] = shapes(
core_arc,
air_gap_arc,
offset,
core_radius.is_sign_positive(),
)?;
return Ok(Self {
length,
core_radius,
air_gap_radius,
angle,
side_thickness,
material,
shape,
center_thickness,
north_shape,
south_shape,
});
}
pub fn core_radius(&self) -> Length {
return self.core_radius;
}
pub fn air_gap_radius(&self) -> Length {
return self.air_gap_radius;
}
pub fn angle(&self) -> f64 {
return self.angle;
}
pub fn center_thickness(&self) -> Length {
return self.center_thickness;
}
pub fn side_thickness(&self) -> Length {
return self.side_thickness;
}
}
#[cfg_attr(feature = "serde", typetag::serde)]
impl Magnet for ArcSegmentMagnet {
fn width(&self) -> Length {
return 2.0
* (self.core_radius() + 0.5 * (self.air_gap_radius() - self.core_radius())).abs()
* (self.angle() / 2.0).sin();
}
fn length(&self) -> Length {
return self.length;
}
fn thickness(&self) -> Length {
return 0.5 * (self.center_thickness + self.side_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),
];
}
}
#[cfg(feature = "serde")]
mod serde_impl {
use super::*;
use serde_mosaic::deserialize_arc_link;
use stem_material::prelude::{deserialize_opt_quantity, deserialize_quantity};
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct WithOrWithoutAirGapRadius {
#[serde(deserialize_with = "deserialize_quantity")]
length: Length,
#[serde(deserialize_with = "deserialize_quantity")]
side_thickness: Length,
#[serde(deserialize_with = "deserialize_quantity")]
core_radius: Length,
#[serde(default, deserialize_with = "deserialize_opt_quantity")]
air_gap_radius: Option<Length>,
#[serde(deserialize_with = "deserialize_angle")]
angle: f64,
#[serde(deserialize_with = "deserialize_arc_link")]
material: Arc<Material>,
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct WithCenterThickness {
#[serde(deserialize_with = "deserialize_quantity")]
length: Length,
#[serde(deserialize_with = "deserialize_quantity")]
core_radius: Length,
#[serde(deserialize_with = "deserialize_quantity")]
center_thickness: Length,
#[serde(deserialize_with = "deserialize_quantity")]
side_thickness: Length,
#[serde(deserialize_with = "deserialize_angle")]
angle: f64,
#[serde(deserialize_with = "deserialize_arc_link")]
material: Arc<Material>,
}
#[derive(deserialize_untagged_verbose_error::DeserializeUntaggedVerboseError)]
enum MagnetEnum {
WithOrWithoutAirGapRadius(WithOrWithoutAirGapRadius),
WithCenterThickness(WithCenterThickness),
}
impl<'de> Deserialize<'de> for ArcSegmentMagnet {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let m = MagnetEnum::deserialize(deserializer)?;
match m {
MagnetEnum::WithOrWithoutAirGapRadius(m) => match m.air_gap_radius {
Some(air_gap_radius) => {
return ArcSegmentMagnet::new(
m.length,
m.core_radius,
air_gap_radius,
m.side_thickness,
m.angle,
m.material,
)
.map_err(serde::de::Error::custom);
}
None => {
return ArcSegmentMagnet::with_const_thickness(
m.length,
m.core_radius,
m.side_thickness,
m.angle,
m.material,
)
.map_err(serde::de::Error::custom);
}
},
MagnetEnum::WithCenterThickness(m) => {
return ArcSegmentMagnet::with_center_thickness(
m.length,
m.core_radius,
m.side_thickness,
m.center_thickness,
m.angle,
m.material,
)
.map_err(serde::de::Error::custom);
}
}
}
}
}