use crate::mesh::MeshBuilder;
use glam::{Affine3A, Mat3A, Quat, Vec2, Vec3, Vec3A};
use std::f32::consts::PI;
use std::ops::Add;
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct Degrees(pub u16);
#[derive(Clone, Debug)]
pub struct Spoke {
pub distance: f32,
pub label: Option<String>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Shading {
Flat,
Smooth,
Ringed,
}
const EMPTY_RING: &[Spoke] = &[Spoke {
distance: 0.0,
label: None,
}];
#[derive(Clone, Debug, PartialEq)]
pub enum Pt {
Vertex(usize),
Branch(String, Vec3),
}
#[derive(Clone, Debug, PartialEq)]
pub struct Point {
pub pt: Pt,
pub order: Degrees,
}
#[derive(Clone, Debug, Default)]
pub struct Ring {
spacing: Option<f32>,
scale: Option<f32>,
shading: Option<Shading>,
spokes: Vec<Spoke>,
xform: Affine3A,
points: Vec<Point>,
}
#[derive(Clone, Copy, Debug)]
pub struct Edge(pub usize, pub usize);
#[derive(Debug, Default)]
pub struct Branch {
internal: Vec<Vec3>,
edges: Vec<Edge>,
}
impl From<f32> for Degrees {
fn from(angle: f32) -> Self {
let deg = angle.to_degrees().rem_euclid(360.0);
Degrees(deg.round() as u16)
}
}
impl Add for Degrees {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Degrees(self.0 + rhs.0 % 360)
}
}
impl Default for Spoke {
fn default() -> Self {
Spoke::from(1.0)
}
}
impl From<f32> for Spoke {
fn from(distance: f32) -> Self {
Spoke {
distance,
label: None,
}
}
}
impl From<&str> for Spoke {
fn from(label: &str) -> Self {
Spoke {
distance: 1.0,
label: Some(label.to_string()),
}
}
}
impl From<(f32, &str)> for Spoke {
fn from(val: (f32, &str)) -> Self {
Spoke {
distance: val.0,
label: Some(val.1.to_string()),
}
}
}
impl Point {
pub fn new(pt: Pt, order: Degrees) -> Self {
Point { pt, order }
}
}
impl Ring {
pub(crate) fn with_branch(branch: Branch, builder: &MeshBuilder) -> Self {
let center = branch.center();
let axis = branch.axis(builder, center);
let xform = Affine3A::from_translation(center);
let count = branch.edges.len();
let mut ring = Ring {
spacing: None,
xform,
scale: None,
shading: None,
spokes: vec![Spoke::default(); count],
points: Vec::new(),
};
ring.transform_rotate(axis);
for (order, vid) in branch.edge_angles(&ring, builder) {
ring.points.push(Point::new(Pt::Vertex(vid), order));
}
ring
}
pub(crate) fn with_ring(&self, ring: &Self) -> Self {
let spacing = ring.spacing.or(self.spacing);
let spokes = if ring.spokes.is_empty() {
self.spokes.clone()
} else {
ring.spokes.clone()
};
let mut ring = Ring {
spacing,
xform: self.xform * ring.xform,
scale: ring.scale.or(self.scale),
shading: ring.shading.or(self.shading),
spokes,
points: Vec::new(),
};
ring.transform_translate();
ring
}
pub fn axis(mut self, axis: Vec3) -> Self {
assert!(axis.x.is_finite());
assert!(axis.y.is_finite());
assert!(axis.z.is_finite());
self.transform_rotate(axis);
self
}
pub fn scale(mut self, scale: f32) -> Self {
assert!(self.points.is_empty(), "cannot scale a branch ring");
assert!(scale.is_finite());
assert!(scale.is_sign_positive());
self.scale = Some(scale);
self
}
pub fn shading(mut self, shading: Shading) -> Self {
self.shading = Some(shading);
self
}
fn scale_or_default(&self) -> f32 {
self.scale.unwrap_or(1.0)
}
pub(crate) fn shading_or_default(&self) -> Shading {
self.shading.unwrap_or(Shading::Smooth)
}
pub fn spoke<S: Into<Spoke>>(mut self, spoke: S) -> Self {
assert!(self.points.is_empty(), "cannot add spoke to a branch ring");
let spoke = spoke.into();
assert!(spoke.distance.is_sign_positive());
assert!(spoke.distance.is_finite());
self.spokes.push(spoke);
self
}
pub(crate) fn spokes(&self) -> impl Iterator<Item = &Spoke> {
if self.spokes.is_empty() {
EMPTY_RING.iter()
} else {
self.spokes[..].iter()
}
}
pub(crate) fn half_step(&self) -> Degrees {
let deg = 180 / self.spokes.len();
Degrees(deg as u16)
}
pub(crate) fn angle(&self, i: usize) -> f32 {
2.0 * PI * i as f32 / self.spokes.len() as f32
}
fn transform_translate(&mut self) {
let spacing = self.spacing.unwrap_or(1.0);
let axis = Vec3A::new(0.0, spacing, 0.0);
self.xform.translation += self.xform.matrix3.mul_vec3a(axis);
}
fn transform_rotate(&mut self, axis: Vec3) {
self.spacing = Some(axis.length());
let axis = axis.normalize();
if axis.x != 0.0 {
let up = Vec2::new(0.0, 1.0);
let proj = Vec2::new(axis.x, axis.y);
let angle = up.angle_between(proj) * proj.length();
self.xform.matrix3 *= Mat3A::from_rotation_z(angle);
}
if axis.z != 0.0 {
let up = Vec2::new(1.0, 0.0);
let proj = Vec2::new(axis.y, axis.z);
let angle = up.angle_between(proj) * proj.length();
self.xform.matrix3 *= Mat3A::from_rotation_x(angle);
}
}
fn make_point(&self, i: usize, spoke: &Spoke) -> (Degrees, Vec3) {
let angle = self.angle(i);
let order = Degrees::from(angle);
let rot = Quat::from_rotation_y(angle);
let distance = spoke.distance * self.scale_or_default();
let pos = rot * Vec3::new(distance, 0.0, 0.0);
let pos = self.xform.transform_point3(pos);
(order, pos)
}
pub(crate) fn make_hub(&self) -> (Degrees, Vec3) {
let pos = self.xform.transform_point3(Vec3::ZERO);
(Degrees(0), pos)
}
pub(crate) fn make_points(&mut self, builder: &mut MeshBuilder) {
let mut points = Vec::with_capacity(self.spokes.len());
for (i, spoke) in self.spokes().enumerate() {
let (order, pos) = self.make_point(i, spoke);
match &spoke.label {
None => {
let vid = builder.push_vtx(pos);
points.push(Point::new(Pt::Vertex(vid), order));
}
Some(label) => {
points.push(Point::new(
Pt::Branch(label.to_string(), pos),
order,
));
}
}
}
self.points = points;
}
pub(crate) fn points(&self) -> impl ExactSizeIterator<Item = &Point> {
self.points.iter()
}
pub(crate) fn points_offset(&self, hs_other: Degrees) -> Vec<Point> {
let mut pts = Vec::with_capacity(self.points.len());
for point in self.points() {
let mut point = point.clone();
point.order = point.order + hs_other;
pts.push(point);
}
pts.sort_by(|a, b| b.order.partial_cmp(&a.order).unwrap());
pts
}
}
impl Branch {
pub fn push_edge(&mut self, v0: usize, v1: usize) {
self.edges.push(Edge(v0, v1));
}
pub fn push_internal(&mut self, pos: Vec3) {
self.internal.push(pos);
}
fn axis(&self, builder: &MeshBuilder, center: Vec3) -> Vec3 {
let mut norm = Vec3::ZERO;
for edge in self.edges() {
let v0 = builder.vertex(edge.0);
let v1 = builder.vertex(edge.1);
norm += (v0 - center).cross(v1 - center);
}
norm.normalize()
}
fn edge_vids(self, edge: usize) -> impl ExactSizeIterator<Item = usize> {
let mut edges = self.edges;
if edge > 0 {
edges.swap(0, edge);
}
let mut vid = edges[0].1;
for i in 1..edges.len() {
for j in (i + 1)..edges.len() {
if vid == edges[j].0 {
edges.swap(i, j);
}
}
vid = edges[i].1;
}
edges.into_iter().map(|e| e.0)
}
fn center(&self) -> Vec3 {
let len = self.internal.len() as f32;
self.internal.iter().fold(Vec3::ZERO, |a, b| a + *b) / len
}
fn edges(&self) -> impl Iterator<Item = &Edge> {
self.edges.iter()
}
fn edge_angles(
self,
ring: &Ring,
builder: &MeshBuilder,
) -> Vec<(Degrees, usize)> {
let inverse = ring.xform.inverse();
let zero_deg = Vec3::new(1.0, 0.0, 0.0);
let mut edge = 0;
let mut angle = f32::MAX;
for (i, ed) in self.edges().enumerate() {
let vid = ed.0;
let pos = inverse.transform_point3(builder.vertex(vid));
let pos = Vec3::new(pos.x, 0.0, pos.z);
let ang = zero_deg.angle_between(pos);
if ang < angle {
angle = ang;
edge = i;
}
}
let vids = self.edge_vids(edge);
let mut angle = 0.0;
let mut ppos = zero_deg;
let mut angles = Vec::with_capacity(vids.len());
for vid in vids {
let pos = inverse.transform_point3(builder.vertex(vid));
let pos = Vec3::new(pos.x, 0.0, pos.z);
let ang = ppos.angle_between(pos);
angle += ang;
let order = Degrees::from(angle);
angles.push((order, vid));
ppos = pos;
}
angles
}
}