Skip to content

Commit

Permalink
Move classification functions to fb/stat.rs.
Browse files Browse the repository at this point in the history
  • Loading branch information
KmolYuan committed Nov 29, 2023
1 parent dc3a6e6 commit 8f103d1
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 233 deletions.
7 changes: 3 additions & 4 deletions four-bar/src/csv.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
//! Functions for reading/writing CSV format.
pub use csv::Error;
use csv::{ReaderBuilder, Writer};
use serde::{de::DeserializeOwned, Serialize};
use std::io::ErrorKind::InvalidData;

/// Parse CSV from string.
pub fn from_reader<R, D>(r: R) -> Result<Vec<D>, Error>
where
R: std::io::Read,
D: DeserializeOwned,
D: serde::de::DeserializeOwned,
{
ReaderBuilder::new()
.has_headers(false)
Expand All @@ -27,7 +26,7 @@ pub fn to_writer<W, C, S>(w: W, c: C) -> Result<(), csv::Error>
where
W: std::io::Write,
C: AsRef<[S]>,
S: Serialize,
S: serde::Serialize,
{
let mut w = Writer::from_writer(w);
c.as_ref().iter().try_for_each(|c| w.serialize(c))?;
Expand All @@ -39,7 +38,7 @@ where
pub fn to_string<C, S>(c: C) -> Result<String, csv::Error>
where
C: AsRef<[S]>,
S: Serialize,
S: serde::Serialize,
{
let mut w = Vec::new();
to_writer(&mut w, c)?;
Expand Down
225 changes: 1 addition & 224 deletions four-bar/src/fb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ pub use self::{
vectorized::*,
};
use crate::efd::EfdDim;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::f64::consts::TAU;

pub mod fb2d;
pub mod fb3d;
Expand All @@ -17,96 +14,6 @@ mod fb_serde;
mod stat;
mod vectorized;

/// Type of the four-bar linkage.
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[allow(clippy::upper_case_acronyms)]
pub enum FourBarTy {
/// Grashof double crank (Drag-link)
GCCC,
/// Grashof crank rocker
GCRR,
/// Grashof double rocker
GRCR,
/// Grashof rocker crank
GRRC,
/// Non-Grashof triple rocker (ground link is the longest)
RRR1,
/// Non-Grashof triple rocker (driver link is the longest)
RRR2,
/// Non-Grashof triple rocker (coupler link is the longest)
RRR3,
/// Non-Grashof triple rocker (follower link is the longest)
RRR4,
/// Invalid
Invalid,
}

impl FourBarTy {
/// Detect from four-bar loop `[l1, l2, l3, l4]`.
pub fn from_loop(mut fb_loop: [f64; 4]) -> Self {
let [l1, l2, l3, l4] = fb_loop;
fb_loop.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap());
let [s, p, q, l] = fb_loop;
if l > s + p + q {
return Self::Invalid;
}
macro_rules! arms {
($d:expr, $c1:expr, $c2:expr, $c3:expr, $c4:expr) => {
match $d {
d if d == l1 => $c1,
d if d == l2 => $c2,
d if d == l3 => $c3,
d if d == l4 => $c4,
_ => unreachable!(),
}
};
}
if s + l < p + q {
arms!(s, Self::GCCC, Self::GCRR, Self::GRCR, Self::GRRC)
} else {
arms!(l, Self::RRR1, Self::RRR2, Self::RRR3, Self::RRR4)
}
}

/// Name of the type.
pub const fn name(&self) -> &'static str {
match self {
Self::GCCC => "Grashof double crank (Drag-link, GCCC)",
Self::GCRR => "Grashof crank rocker (GCRR)",
Self::GRCR => "Grashof double rocker (GRCR)",
Self::GRRC => "Grashof rocker crank (GRRC)",
Self::RRR1 => "Non-Grashof triple rocker (RRR1)",
Self::RRR2 => "Non-Grashof triple rocker (RRR2)",
Self::RRR3 => "Non-Grashof triple rocker (RRR3)",
Self::RRR4 => "Non-Grashof triple rocker (RRR4)",
Self::Invalid => "Invalid",
}
}

/// Check if the type is valid.
pub const fn is_valid(&self) -> bool {
!matches!(self, Self::Invalid)
}

/// Return true if the type is Grashof linkage.
pub const fn is_grashof(&self) -> bool {
matches!(self, Self::GCCC | Self::GCRR | Self::GRCR | Self::GRRC)
}

/// Return true if the type has continuous motion.
pub const fn is_closed_curve(&self) -> bool {
matches!(self, Self::GCCC | Self::GCRR)
}

/// Return true if the type has non-continuous motion.
pub const fn is_open_curve(&self) -> bool {
matches!(
self,
Self::GRCR | Self::GRRC | Self::RRR1 | Self::RRR2 | Self::RRR3 | Self::RRR4
)
}
}

/// Four-bar base.
#[derive(Clone, Default, Debug, PartialEq)]
pub struct FourBarBase<UN, NM> {
Expand Down Expand Up @@ -226,137 +133,6 @@ pub trait Transformable<D: efd::EfdDim>: Sized {
}
}

/// Planar loop of the linkage.
pub trait PlanarLoop {
/// Get the planar loop.
fn planar_loop(&self) -> [f64; 4];

/// Return the type of this linkage.
fn ty(&self) -> FourBarTy {
FourBarTy::from_loop(self.planar_loop())
}

/// Input angle bounds of the linkage.
fn angle_bound(&self) -> AngleBound
where
Self: Statable,
{
let stat = self.stat();
AngleBound::from_planar_loop(self.planar_loop(), stat)
}

/// Check if the range of motion has two branches.
fn has_branch(&self) -> bool {
let mut planar_loop @ [l1, l2, l3, l4] = self.planar_loop();
planar_loop.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap());
planar_loop[3] < planar_loop[..3].iter().sum()
&& l1 + l2 > l3 + l4
&& (l1 - l2).abs() < (l3 - l4).abs()
}
}

/// Angle boundary types. The input angle range.
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Copy, Clone, PartialEq, Default, Debug)]
pub enum AngleBound {
/// Closed curve
Closed,
/// Open curve (`[start, end]`)
Open([f64; 2]),
/// Open curve with branch (`[[start, end]; 2]`)
OpenBranch([f64; 2]),
/// Invalid
#[default]
Invalid,
}

impl AngleBound {
/// The minimum input angle bound. (π/2)
pub const MIN_ANGLE: f64 = std::f64::consts::FRAC_PI_2;

/// Check angle bound from a planar loop.
pub fn from_planar_loop(mut planar_loop: [f64; 4], stat: Stat) -> Self {
let [l1, l2, l3, l4] = planar_loop;
planar_loop.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap());
if planar_loop[3] > planar_loop[..3].iter().sum() {
return Self::Invalid;
}
match (l1 + l2 <= l3 + l4, (l1 - l2).abs() >= (l3 - l4).abs()) {
(true, true) => Self::Closed,
(true, false) => {
let l33 = l3 - l4;
let d = (l1 * l1 + l2 * l2 - l33 * l33) / (2. * l1 * l2);
Self::Open([d.acos(), TAU - d.acos()])
}
(false, true) => {
let l33 = l3 + l4;
let d = (l1 * l1 + l2 * l2 - l33 * l33) / (2. * l1 * l2);
Self::Open([-d.acos(), d.acos()])
}
(false, false) => {
let numerator = l1 * l1 + l2 * l2;
let denominator = 2. * l1 * l2;
let l33 = l3 - l4;
let d1 = (numerator - l33 * l33) / denominator;
let l33 = l3 + l4;
let d2 = (numerator - l33 * l33) / denominator;
if stat.is_c1() {
Self::OpenBranch([d1.acos(), d2.acos()])
} else {
Self::OpenBranch([TAU - d2.acos(), TAU - d1.acos()])
}
}
}
}

/// Check there has two branches.
pub fn has_branch(&self) -> bool {
matches!(self, Self::OpenBranch(_))
}

/// Create a open and its reverse angle bound.
pub fn open_and_rev_at(a: f64, b: f64) -> [Self; 2] {
[Self::Open([a, b]), Self::Open([b, a])]
}

/// Check the state is the same to the provided mode.
pub fn check_mode(self, is_open: bool) -> Self {
match (&self, is_open) {
(Self::Closed, false) | (Self::Open(_), true) => self,
_ => Self::Invalid,
}
}

/// Angle range must greater than [`AngleBound::MIN_ANGLE`].
pub fn check_min(self) -> Self {
match self {
Self::Open([a, b]) | Self::OpenBranch([a, b]) => {
let b = if b > a { b } else { b + TAU };
if b - a > Self::MIN_ANGLE {
self
} else {
Self::Invalid
}
}
_ => self,
}
}

/// Turn into boundary values.
pub fn to_value(self) -> Option<[f64; 2]> {
match self {
Self::Closed => Some([0., TAU]),
Self::Open(a) | Self::OpenBranch(a) => Some(a),
Self::Invalid => None,
}
}

/// Check if the data is valid.
pub fn is_valid(&self) -> bool {
!matches!(self, Self::Invalid)
}
}

/// Curve-generating behavior.
pub trait CurveGen<D: efd::EfdDim>: PlanarLoop + Statable {
/// Get the position with input angle.
Expand Down Expand Up @@ -420,6 +196,7 @@ where
F: Fn(f64) -> Option<[C; 5]>,
M: Fn([C; 5]) -> B + Copy,
{
use std::f64::consts::TAU;
let end = if end > start { end } else { end + TAU };
let interval = (end - start) / res as f64;
let mut iter = (0..res).map(move |n| start + n as f64 * interval).map(f);
Expand Down
4 changes: 2 additions & 2 deletions four-bar/src/fb/fb2d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::f64::consts::FRAC_PI_6;
/// Unnormalized part of four-bar linkage.
///
/// Please see [`FourBar`] for more information.
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[derive(Clone, Debug, PartialEq, Default)]
pub struct UnNorm {
/// X offset of the driver link pivot
Expand Down Expand Up @@ -53,7 +53,7 @@ impl UnNorm {
/// + Extanded link `l5`
/// + Coupler link angle `g`
/// + Inverse coupler and follower to another circuit
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[derive(Clone, Debug, PartialEq, Default)]
pub struct NormFourBar {
/// Length of the ground link
Expand Down
4 changes: 2 additions & 2 deletions four-bar/src/fb/fb3d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::f64::consts::{FRAC_PI_2, FRAC_PI_4, PI, TAU};
/// Unnormalized part of spherical four-bar linkage.
///
/// Please see [`SFourBar`] for more information.
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[derive(Clone, Debug, PartialEq, Default)]
pub struct UnNorm {
/// X offset of the sphere center
Expand Down Expand Up @@ -59,7 +59,7 @@ impl UnNorm {
/// + Follower link `l4`
/// + Extanded link `l5`
/// + Coupler link angle `g`
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[derive(Clone, Debug, PartialEq, Default)]
pub struct SNormFourBar {
/// Length of the ground link
Expand Down
Loading

0 comments on commit 8f103d1

Please sign in to comment.