use ambassador::Delegate;
use derive_builder::Builder;
use derive_more::derive::From;
use nalgebra as na;
use crate::{
common::{AffineMatrix3D, DimensionType as _, Point3D, Unit},
internal::generate_sentence_repr,
scad_display::{ambassador_impl_ScadDisplay, ScadDisplay},
value_type::Angle,
};
use crate::common::D3;
macro_rules! __impl_apply_2d {
($mod_ty:ident) => {
__impl_apply_to_modifier!(
apply_to,
$mod_ty,
$crate::scad_3d::ScadModifierBody3D,
$crate::common::ScadObjectGeneric<$crate::common::D3>,
$crate::scad_3d::ScadModifier3D,
$crate::scad_3d::ScadObject3D,
$crate::common::ScadObjectImpl::Object3D,
$crate::common::D3, $crate::common::D2 );
__impl_panicking_modifier_methods!($mod_ty, D3);
};
}
macro_rules! __impl_apply_3d {
($mod_ty:ident) => {
__impl_apply_to_modifier!(
apply_to,
$mod_ty,
$crate::scad_3d::ScadModifierBody3D,
$crate::common::ScadObjectGeneric<$crate::common::D3>,
$crate::scad_3d::ScadModifier3D,
$crate::scad_3d::ScadObject3D,
$crate::common::ScadObjectImpl::Object3D,
$crate::common::D3, $crate::common::D3 );
__impl_panicking_modifier_methods!($mod_ty, D3);
};
}
#[derive(Builder, Debug, Clone, Copy)]
pub struct Translate3D {
#[builder(setter(into))]
pub v: Point3D,
}
__impl_builder_modifier!(Translate3D);
__impl_modifier_chaining!(Translate3D);
__impl_apply_3d!(Translate3D);
impl ScadDisplay for Translate3D {
fn repr_scad(&self) -> String {
generate_sentence_repr("translate", __generate_scad_options!(("", self.v);))
}
}
#[derive(Copy, Clone, Debug, PartialEq, From, Delegate)]
#[delegate(ScadDisplay)]
pub enum Rotate3DAngle {
A(Angle),
V(na::Vector3<Angle>),
}
#[derive(Copy, Clone, Debug, PartialEq, From)]
pub enum Rotate3DAngleEntry {
Single(Unit),
Triple([Unit; 3]),
}
#[derive(Builder, Debug, Clone, Copy)]
pub struct Rotate3D {
#[builder(setter(custom))]
pub a: Rotate3DAngle,
#[builder(setter(into, strip_option), default)]
pub v: Option<Point3D>,
}
__impl_builder_modifier!(Rotate3D);
__impl_modifier_chaining!(Rotate3D);
__impl_apply_3d!(Rotate3D);
impl Rotate3DBuilder {
pub fn deg<T: Into<Rotate3DAngleEntry>>(&mut self, value: T) -> &mut Self {
let new = self;
new.a = match value.into() {
Rotate3DAngleEntry::Single(a) => Some(Rotate3DAngle::A(Angle::Deg(a))),
Rotate3DAngleEntry::Triple(a) => Some(Rotate3DAngle::V(na::Vector3::from_iterator(
a.into_iter().map(Angle::Deg),
))),
};
new
}
pub fn rad<T: Into<Rotate3DAngleEntry>>(&mut self, value: T) -> &mut Self {
let new = self;
new.a = match value.into() {
Rotate3DAngleEntry::Single(a) => Some(Rotate3DAngle::A(Angle::Rad(a))),
Rotate3DAngleEntry::Triple(a) => Some(Rotate3DAngle::V(na::Vector3::from_iterator(
a.into_iter().map(Angle::Rad),
))),
};
new
}
}
impl ScadDisplay for Rotate3D {
fn repr_scad(&self) -> String {
generate_sentence_repr(
"rotate",
__generate_scad_options!(
("a", self.a); opt:(("v", self.v);)
),
)
}
}
#[derive(Builder, Debug, Clone, Copy)]
pub struct Scale3D {
#[builder(setter(into))]
pub v: Point3D,
}
__impl_builder_modifier!(Scale3D);
__impl_modifier_chaining!(Scale3D);
__impl_apply_3d!(Scale3D);
impl ScadDisplay for Scale3D {
fn repr_scad(&self) -> String {
generate_sentence_repr("scale", __generate_scad_options!(("", self.v);))
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, From, Delegate)]
#[delegate(ScadDisplay)]
pub enum ResizeAuto3D {
B(bool),
V([bool; 3]),
}
#[derive(Builder, Debug, Clone, Copy)]
pub struct Resize3D {
#[builder(setter(into))]
pub size: Point3D,
#[builder(setter(into, strip_option), default)]
pub auto: Option<ResizeAuto3D>,
}
__impl_builder_modifier!(Resize3D);
__impl_modifier_chaining!(Resize3D);
__impl_apply_3d!(Resize3D);
impl ScadDisplay for Resize3D {
fn repr_scad(&self) -> String {
generate_sentence_repr(
"resize",
__generate_scad_options!(
("", self.size); opt:(("auto", self.auto);)
),
)
}
}
#[derive(Builder, Debug, Clone, Copy)]
pub struct Mirror3D {
#[builder(setter(into))]
pub v: Point3D,
}
__impl_builder_modifier!(Mirror3D);
__impl_modifier_chaining!(Mirror3D);
__impl_apply_3d!(Mirror3D);
impl ScadDisplay for Mirror3D {
fn repr_scad(&self) -> String {
generate_sentence_repr("mirror", __generate_scad_options!(("", self.v);))
}
}
#[derive(Builder, Debug, Clone, Copy)]
pub struct MultMatrix3D {
#[builder(setter(into))]
pub m: AffineMatrix3D,
}
__impl_builder_modifier!(MultMatrix3D);
__impl_modifier_chaining!(MultMatrix3D);
__impl_apply_3d!(MultMatrix3D);
impl ScadDisplay for MultMatrix3D {
fn repr_scad(&self) -> String {
generate_sentence_repr("multmatrix", __generate_scad_options!(("m", self.m);))
}
}
#[derive(Builder, Debug, Clone, Copy)]
pub struct LinearExtrude {
#[builder(setter(into), default)]
pub height: Unit,
#[builder(setter(into, strip_option), default)]
pub v: Option<Point3D>,
#[builder(setter(into, strip_option), default)]
pub center: Option<bool>,
#[builder(setter(into, strip_option), default)]
pub twist: Option<Unit>,
#[builder(setter(into, strip_option), default)]
pub convexity: Option<u64>,
#[builder(setter(into, strip_option), default)]
pub slices: Option<u64>,
#[builder(setter(into, strip_option), default)]
pub scale: Option<Unit>,
#[builder(setter(into, strip_option), default)]
pub r#fn: Option<u64>,
}
__impl_builder_modifier!(LinearExtrude);
__impl_modifier_chaining!(LinearExtrude);
__impl_apply_2d!(LinearExtrude);
impl ScadDisplay for LinearExtrude {
fn repr_scad(&self) -> String {
generate_sentence_repr(
"linear_extrude",
__generate_scad_options!(
("height", self.height);
opt: (
("v", self.v);
("center", self.center);
("twist", self.twist);
("convexity", self.convexity);
("slices", self.slices);
("scale", self.scale);
("$fn", self.r#fn);
)
),
)
}
}
#[derive(Builder, Debug, Clone, Copy)]
pub struct RotateExtrude {
#[builder(setter(into, strip_option), default)]
pub angle: Option<Unit>,
#[builder(setter(into, strip_option), default)]
pub start: Option<Unit>,
#[builder(setter(into, strip_option), default)]
pub convexity: Option<u64>,
#[builder(setter(into, strip_option), default)]
pub fa: Option<Unit>,
#[builder(setter(into, strip_option), default)]
pub r#fn: Option<u64>,
#[builder(setter(into, strip_option), default)]
pub fs: Option<Unit>,
}
__impl_builder_modifier!(RotateExtrude);
__impl_modifier_chaining!(RotateExtrude);
__impl_apply_2d!(RotateExtrude);
impl ScadDisplay for RotateExtrude {
fn repr_scad(&self) -> String {
generate_sentence_repr(
"rotate_extrude",
__generate_scad_options!(
opt: (
("angle", self.angle);
("start", self.start);
("convexity", self.convexity);
("$fa", self.fa);
("$fn", self.r#fn);
("$fs", self.fs);
)
),
)
}
}
#[cfg(test)]
mod tests {
use std::f64::consts::PI;
use super::*;
#[test]
fn test_translate3d() {
assert_eq!(
Translate3DBuilder::default()
.v([8., -4., 6.])
.build()
.unwrap()
.repr_scad(),
"translate([8, -4, 6])"
);
}
#[test]
fn test_rotate3d() {
assert_eq!(
Rotate3DBuilder::default()
.deg([45., 0., 90.])
.build()
.unwrap()
.repr_scad(),
"rotate(a = [45, 0, 90])"
);
assert_eq!(
Rotate3DBuilder::default()
.rad([PI / 4., 0., PI / 2.])
.build()
.unwrap()
.repr_scad(),
"rotate(a = [45, 0, 90])"
);
assert_eq!(
Rotate3DBuilder::default()
.rad(PI / 4.)
.v([1., 1., 0.])
.build()
.unwrap()
.repr_scad(),
"rotate(a = 45, v = [1, 1, 0])"
);
}
#[test]
fn test_mirror3d() {
assert_eq!(
Mirror3DBuilder::default()
.v([1., -1., 0.])
.build()
.unwrap()
.repr_scad(),
"mirror([1, -1, 0])"
);
}
#[test]
fn test_scale3d() {
assert_eq!(
Scale3DBuilder::default()
.v([3., 2., 4.])
.build()
.unwrap()
.repr_scad(),
"scale([3, 2, 4])"
);
}
#[test]
fn test_resize3d() {
let mut r1 = Resize3DBuilder::default();
_ = r1.size([3., 2., 1.]);
assert_eq!(r1.clone().build().unwrap().repr_scad(), "resize([3, 2, 1])");
assert_eq!(
r1.clone().auto(true).build().unwrap().repr_scad(),
"resize([3, 2, 1], auto = true)"
);
assert_eq!(
r1.auto([true, false, true]).build().unwrap().repr_scad(),
"resize([3, 2, 1], auto = [true, false, true])"
);
}
#[test]
fn test_multimatrix2d() {
let m = AffineMatrix3D::new(1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12.);
assert_eq!(
MultMatrix3DBuilder::default()
.m(m)
.build()
.unwrap()
.repr_scad(),
"multmatrix(m = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])"
);
}
#[test]
fn test_linear_extrude() {
assert_eq!(
LinearExtrudeBuilder::default()
.height(5.)
.build()
.unwrap()
.repr_scad(),
"linear_extrude(height = 5)"
);
assert_eq!(
LinearExtrudeBuilder::default()
.height(5.)
.v([0., 0.2, 1.])
.center(true)
.twist(180.)
.convexity(10_u64)
.slices(30_u64)
.scale(0.7)
.r#fn(20_u64)
.build()
.unwrap()
.repr_scad(),
"linear_extrude(height = 5, v = [0, 0.2, 1], center = true, twist = 180, convexity = 10, slices = 30, scale = 0.7, $fn = 20)"
);
}
#[test]
fn test_rotate_extrude() {
assert_eq!(
RotateExtrudeBuilder::default().build().unwrap().repr_scad(),
"rotate_extrude()"
);
assert_eq!(
RotateExtrudeBuilder::default()
.angle(180.)
.start(90.)
.convexity(10_u64)
.fa(5.)
.build()
.unwrap()
.repr_scad(),
"rotate_extrude(angle = 180, start = 90, convexity = 10, $fa = 5)"
);
}
}