use bevy::math::Vec3;
use super::state::{BASE, BASE_COLLAR, COLLAR, GrowthState, TOP_CAP, WOOD};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[repr(u32)]
pub(crate) enum Class {
#[default]
Tube = 0,
Leaf = 1,
Collar = 2,
Base = 3,
BaseCollar = 4,
RootFar = 5,
RootFarCollar = 6,
Trunk = 7,
TrunkTopCap = 8,
TrunkCone = 9,
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub(crate) struct Tag {
pub node: u32,
pub class: Class,
pub ordinal: u32,
pub axis: Vec3,
pub f: f32,
pub offset: Vec3,
pub extra: f32,
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct Placement {
pub anchor: Vec3,
pub scale: f32,
#[cfg_attr(not(test), allow(dead_code))] pub on: bool,
#[cfg_attr(not(test), allow(dead_code))]
pub collapse: Vec3,
pub twist: f32,
}
fn lerp(a: f32, b: f32, t: f32) -> f32 {
a + (b - a) * t
}
pub(crate) fn twisted(v: Vec3, twist: f32) -> Vec3 {
let (sin, cos) = twist.sin_cos();
Vec3::new(v.x * cos + v.z * sin, v.y, -v.x * sin + v.z * cos)
}
pub(crate) fn placement(tag: &Tag, state: &GrowthState) -> Placement {
let node = &state.nodes[tag.node as usize];
let on_axis = node.start + node.length * tag.axis;
let wood = |anchor: Vec3, scale: f32, flag: u32| Placement {
anchor,
scale,
on: node.flags & flag != 0,
collapse: node.start,
twist: 0.0,
};
match tag.class {
Class::Tube => wood(on_axis, lerp(node.base_radius, node.tip_radius, tag.f), WOOD),
Class::Leaf => {
let shown = node.leaf_scale > 0.0 && (node.leaf_count < 0.0 || (tag.ordinal as f32) < node.leaf_count);
Placement {
anchor: on_axis,
scale: if shown { node.leaf_scale } else { 0.0 },
on: true,
collapse: on_axis,
twist: 0.0,
}
}
Class::Collar => {
let smooth = tag.f * tag.f * (3.0 - 2.0 * tag.f);
wood(
node.collar_start.lerp(node.collar_end, tag.f),
lerp(node.collar_start_radius, node.collar_end_radius, smooth),
COLLAR,
)
}
Class::Base => wood(node.start, node.base_radius, BASE),
Class::BaseCollar => wood(node.collar_start, node.collar_start_radius, BASE_COLLAR),
Class::RootFar => wood(node.root, node.base_radius, BASE),
Class::RootFarCollar => wood(node.root, node.collar_start_radius, BASE_COLLAR),
Class::Trunk | Class::TrunkTopCap | Class::TrunkCone => {
let ring = &state.rings[tag.f as usize];
Placement {
anchor: ring.center,
scale: ring.radius + tag.extra * ring.bulge,
on: tag.class != Class::TrunkTopCap || state.trunk_flags & TOP_CAP != 0,
collapse: ring.center,
twist: ring.twist,
}
}
}
}
#[cfg(test)]
pub(crate) fn position(tag: &Tag, state: &GrowthState) -> Vec3 {
use super::state::CONE;
let p = placement(tag, state);
if tag.class == Class::TrunkCone {
return if state.trunk_flags & CONE != 0 {
p.anchor + Vec3::Y * state.cone_lift + tag.offset
} else {
p.anchor + p.scale * twisted(tag.axis, p.twist)
};
}
if p.on { p.anchor + p.scale * twisted(tag.offset, p.twist) } else { p.collapse }
}