use crate::{
figure::SegmentItem,
math::Build,
parser::Type,
token::{number::ProcNum, Span},
unroll::{figure::PCNode, AnyExpr, PointCollection, PointCollectionData},
};
use super::{prelude::*, Overload};
use geo_aid_figure::math_string::MathString;
use num_traits::{ToPrimitive, Zero};
fn poly(
params: &[AnyExpr],
context: &CompileContext,
mut props: Properties,
) -> Expr<PointCollection> {
let NumberData::Number(num) = ¶ms[0].as_number().unwrap().get_data().data else {
unreachable!()
};
let n = num.0.re.to_integer().to_usize().unwrap();
let points = (0..n).map(|_| context.free_point()).collect::<Vec<_>>();
let mut expr = Expr {
data: Rc::new(PointCollection {
length: n,
data: PointCollectionData::PointCollection(points.into()),
}),
span: Span::empty(),
node: None,
};
let node = PCNode {
display: props.get("display").maybe_unset(true),
children: (0..n).map(|_| None).collect(),
props: None,
expr: expr.clone_without_node(),
};
let mut node = HierarchyNode::new(node);
node.set_associated(Associated);
node.insert_data(
"display_segments",
props.get("displaysegments").maybe_unset(true),
);
node.insert_data("style", props.get("style").maybe_unset(Style::Solid));
node.root.props = Some(props);
expr.node = Some(node);
expr
}
#[derive(Debug)]
struct Poly;
impl Overload for Poly {
fn get_returned_type(&self, params: &[AnyExpr]) -> Option<Type> {
if params.len() != 1 {
return None;
}
let NumberData::Number(num) = ¶ms[0].as_number()?.get_data().data else {
return None;
};
let n = if num.0.im.is_zero() && num.0.re.is_integer() {
num.0.re.to_integer().to_usize()?
} else {
return None;
};
if n <= 2 {
return None;
}
Some(Type::PointCollection(n))
}
fn unroll(
&self,
params: Vec<AnyExpr>,
context: &mut CompileContext,
props: Properties,
) -> AnyExpr {
AnyExpr::PointCollection(poly(¶ms, context, props))
}
}
#[derive(Debug)]
pub struct Associated;
impl BuildAssociated<PCNode> for Associated {
fn build_associated(
self: Box<Self>,
build: &mut Build,
associated: &mut HierarchyNode<PCNode>,
) {
let display_segments = associated
.get_data("display_segments")
.unwrap()
.as_bool()
.unwrap();
let style = associated.get_data("style").unwrap().as_style().unwrap();
if display_segments.unwrap() {
let PointCollectionData::PointCollection(points) =
&associated.root.expr.get_data().data
else {
unreachable!()
};
let mut pts: Vec<_> = points.iter().map(|pt| build.load(pt)).collect();
pts.push(build.load(&points[0]));
for i in 1..pts.len() {
build.add(SegmentItem {
p_id: pts[i].clone(),
q_id: pts[i - 1].clone(),
label: MathString::new(),
style: style.unwrap(),
})
}
}
}
}
struct Convex;
impl Overload for Convex {
fn get_returned_type(&self, params: &[AnyExpr]) -> Option<Type> {
Poly.get_returned_type(params)
}
fn unroll(
&self,
params: Vec<AnyExpr>,
context: &mut CompileContext,
props: Properties,
) -> AnyExpr {
let pc = poly(¶ms, context, props);
let len = pc.data.length;
if len > 3 {
for i in 3..len {
let alpha = context.angle_dir(
pc.index_without_node(i - 2),
pc.index_without_node(0),
pc.index_without_node(i - 1),
);
let beta = context.angle_dir(
pc.index_without_node(i - 1),
pc.index_without_node(0),
pc.index_without_node(i),
);
let product = context.mult(alpha, beta);
context.gt(product, number!(ANGLE ProcNum::zero()), false);
}
for i in 1..len {
let i_plus_1 = (i + 1) % len;
let i_plus_2 = (i + 2) % len;
let product = context.mult(
context.angle_dir(
index!(no-node pc, i),
index!(no-node pc, i-1),
index!(no-node pc, i_plus_1),
),
context.angle_dir(
index!(no-node pc, i_plus_1),
index!(no-node pc, i),
index!(no-node pc, i_plus_2),
),
);
context.gt(product, number!(ANGLE ProcNum::zero()), false);
}
}
pc.into()
}
}
pub fn register(library: &mut Library) {
library
.add(Function::new("poly").alias("polygon").overload(Poly))
.add(
Function::new("convex")
.alias("convexpolygon")
.alias("convexpoly")
.overload(Convex),
);
}