use std::marker::PhantomData;
use crate::{Id, IdGenerator, datatypes::inputs::DatumPoint, datatypes::outputs::Point};
const VARS_PER_POINT: usize = 2;
const VARS_PER_CIRCLE: usize = 3;
pub const VARS_PER_ARC: usize = 6;
#[derive(Clone, Debug)]
pub struct GeometryVariables<S> {
variables: Vec<(Id, f64)>,
num_points: usize,
num_circles: usize,
num_arcs: usize,
state: PhantomData<S>,
}
impl<S> Default for GeometryVariables<S> {
fn default() -> Self {
Self {
variables: Default::default(),
num_points: Default::default(),
num_circles: Default::default(),
num_arcs: Default::default(),
state: Default::default(),
}
}
}
pub trait State {}
pub struct PointsState;
impl State for PointsState {}
pub struct CirclesState;
impl State for CirclesState {}
pub struct ArcsState;
impl State for ArcsState {}
#[derive(Clone)]
pub struct DoneState;
impl State for DoneState {}
impl<S: State> GeometryVariables<S> {
pub fn len(&self) -> usize {
self.variables.len()
}
pub fn variables(&self) -> Vec<(Id, f64)> {
self.variables.clone()
}
fn push_scalar(&mut self, id_generator: &mut IdGenerator, guess: f64) {
self.variables.push((id_generator.next_id(), guess));
}
pub fn point_ids(&self, point_id: usize) -> PointVars {
let x = self.variables[VARS_PER_POINT * point_id].0;
let y = self.variables[VARS_PER_POINT * point_id + 1].0;
PointVars { x, y }
}
pub fn circle_ids(&self, circle_id: usize) -> CircleVars {
let start_of_circles = VARS_PER_POINT * self.num_points;
let x = self.variables[start_of_circles + VARS_PER_CIRCLE * circle_id].0;
let y = self.variables[start_of_circles + VARS_PER_CIRCLE * circle_id + 1].0;
let radius = self.variables[start_of_circles + VARS_PER_CIRCLE * circle_id + 2].0;
CircleVars {
center: PointVars { x, y },
radius,
}
}
pub fn arc_ids(&self, arc_id: usize) -> ArcVars {
let start_of_arcs = VARS_PER_POINT * self.num_points;
let ax = self.variables[start_of_arcs + VARS_PER_ARC * arc_id].0;
let ay = self.variables[start_of_arcs + VARS_PER_ARC * arc_id + 1].0;
let start = PointVars { x: ax, y: ay };
let bx = self.variables[start_of_arcs + VARS_PER_ARC * arc_id + 2].0;
let by = self.variables[start_of_arcs + VARS_PER_ARC * arc_id + 3].0;
let end = PointVars { x: bx, y: by };
let cx = self.variables[start_of_arcs + VARS_PER_ARC * arc_id + 4].0;
let cy = self.variables[start_of_arcs + VARS_PER_ARC * arc_id + 5].0;
let center = PointVars { x: cx, y: cy };
ArcVars { start, end, center }
}
}
impl GeometryVariables<PointsState> {
pub fn push_point(&mut self, id_generator: &mut IdGenerator, x: f64, y: f64) {
assert!(self.num_circles == 0, "You must add points before circles");
assert!(self.num_arcs == 0, "You must add points before arcs");
self.num_points += 1;
self.push_scalar(id_generator, x);
self.push_scalar(id_generator, y);
}
pub fn done(self) -> GeometryVariables<CirclesState> {
GeometryVariables {
variables: self.variables,
num_points: self.num_points,
num_circles: self.num_circles,
num_arcs: self.num_arcs,
state: PhantomData,
}
}
}
impl GeometryVariables<CirclesState> {
pub fn push_circle(
&mut self,
id_generator: &mut IdGenerator,
center_x: f64,
center_y: f64,
radius: f64,
) {
assert!(self.num_arcs == 0, "You must add circles before arcs");
self.num_circles += 1;
self.variables.push((id_generator.next_id(), center_x));
self.variables.push((id_generator.next_id(), center_y));
self.variables.push((id_generator.next_id(), radius));
}
pub fn done(self) -> GeometryVariables<ArcsState> {
GeometryVariables {
variables: self.variables,
num_points: self.num_points,
num_circles: self.num_circles,
num_arcs: self.num_arcs,
state: PhantomData,
}
}
}
impl GeometryVariables<ArcsState> {
pub fn push_arc(&mut self, id_generator: &mut IdGenerator, a: Point, b: Point, center: Point) {
self.num_arcs += 1;
let c = center;
self.variables.push((id_generator.next_id(), a.x));
self.variables.push((id_generator.next_id(), a.y));
self.variables.push((id_generator.next_id(), b.x));
self.variables.push((id_generator.next_id(), b.y));
self.variables.push((id_generator.next_id(), c.x));
self.variables.push((id_generator.next_id(), c.y));
}
pub fn done(self) -> GeometryVariables<DoneState> {
GeometryVariables {
variables: self.variables,
num_points: self.num_points,
num_circles: self.num_circles,
num_arcs: self.num_arcs,
state: PhantomData,
}
}
}
pub struct PointVars {
pub x: Id,
pub y: Id,
}
#[allow(clippy::from_over_into)]
impl Into<DatumPoint> for PointVars {
fn into(self) -> DatumPoint {
DatumPoint {
x_id: self.x,
y_id: self.y,
}
}
}
pub struct CircleVars {
pub center: PointVars,
pub radius: Id,
}
pub struct ArcVars {
pub start: PointVars,
pub end: PointVars,
pub center: PointVars,
}