use bevy::{
prelude::*,
render::mesh::{Indices, PrimitiveTopology},
sprite::Mesh2dHandle,
};
#[derive(Debug, Clone, Component)]
pub struct RadialBar2D {
pub hole_radius: f32,
pub circle_radius: f32,
pub value: f32,
max_value: f32,
segment_count: usize,
pub mesh_handle: Mesh2dHandle,
pub color_handle: Handle<ColorMaterial>,
}
impl RadialBar2D {
const FULL_ROTATION_RAD: f32 = 6.28318530718;
const NORMAL: [f32; 3] = [0., 0., 1.];
const UV: [f32; 2] = [0., 1.];
pub fn new(
hole_radius: f32,
circle_radius: f32,
value: f32,
max_value: f32,
segment_count: usize,
color: Color,
meshes: &mut ResMut<Assets<Mesh>>,
materials: &mut ResMut<Assets<ColorMaterial>>,
) -> Self {
let vertices = RadialBar2D::generate_vertices(circle_radius, hole_radius, segment_count);
let tri_indices = RadialBar2D::form_valuebar_triangles(segment_count, max_value, value);
let mut mesh = Mesh::new(PrimitiveTopology::TriangleList);
let mut normals: Vec<[f32; 3]> = vec![];
let mut uvs: Vec<[f32; 2]> = vec![];
for _vertex in 0..vertices.len() {
normals.push(RadialBar2D::NORMAL.clone());
uvs.push(RadialBar2D::UV.clone());
}
mesh.insert_attribute(
Mesh::ATTRIBUTE_POSITION,
vertices
.iter()
.map(|x| x.to_array())
.collect::<Vec<[f32; 3]>>(),
);
mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals);
mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs);
let color_handle = materials.add(ColorMaterial::from(color));
let mut flattened_indices: Vec<u32> = vec![];
for tri in tri_indices.iter() {
flattened_indices.extend_from_slice(tri);
}
mesh.set_indices(Some(Indices::U32(flattened_indices)));
let handler = Mesh2dHandle(meshes.add(mesh));
RadialBar2D {
hole_radius,
circle_radius,
value,
max_value,
segment_count,
mesh_handle: handler,
color_handle,
}
}
pub fn generate_vertices(
circle_radius: f32,
hole_radius: f32,
segment_count: usize,
) -> Vec<Vec3> {
let mut vertices: Vec<Vec3> = vec![];
for rot in 0..segment_count {
let mut outer = Vec3::new(0., circle_radius, 0.);
let mut inner = Vec3::new(0., hole_radius, 0.);
outer = RadialBar2D::calculate_position(
&outer,
(rot as f32 / segment_count as f32) * RadialBar2D::FULL_ROTATION_RAD,
);
inner = RadialBar2D::calculate_position(
&inner,
(rot as f32 / segment_count as f32) * RadialBar2D::FULL_ROTATION_RAD,
);
vertices.push(outer);
vertices.push(inner);
}
vertices
}
fn calculate_position(pos: &Vec3, rad: f32) -> Vec3 {
let mut result = *pos;
let x = result.x;
let y = result.y;
let x_ = x * rad.cos() - y * rad.sin();
let y_ = y * rad.cos() + x * rad.sin();
result.x = x_;
result.y = y_;
result
}
pub fn form_triangles(segment_count: usize) -> Vec<[u32; 3]> {
let mut triangles: Vec<[u32; 3]> = vec![];
for segment_index in 0..segment_count {
triangles.push([
(segment_index * 2) as u32,
((segment_index * 2 + 2) % (segment_count * 2)) as u32,
((segment_index * 2 + 2) % (segment_count * 2) + 1) as u32,
]);
triangles.push([
(segment_index * 2) as u32,
((segment_index * 2 + 2) % (segment_count * 2) + 1) as u32,
((segment_index * 2) + 1) as u32,
]);
}
triangles
}
pub fn form_valuebar_triangles(
segment_count: usize,
max_value: f32,
value: f32,
) -> Vec<[u32; 3]> {
let mut triangles = RadialBar2D::form_triangles(segment_count);
let value_float = value / max_value;
let detached_value_segments = ((1. - value_float) * segment_count as f32).floor() as usize;
if detached_value_segments > 0 {
for _segment in 0..detached_value_segments {
triangles.pop();
triangles.pop();
}
}
triangles
}
pub fn regenerate_mesh(&self, meshes: &mut ResMut<Assets<Mesh>>) -> Mesh2dHandle {
let mut indices = vec![];
for tri in
RadialBar2D::form_valuebar_triangles(self.segment_count, self.max_value, self.value)
{
indices.extend_from_slice(&tri);
}
if let Some(mesh) = meshes.get_mut(&self.mesh_handle.0) {
mesh.set_indices(Some(Indices::U32(indices)));
}
self.mesh_handle.clone()
}
pub fn add_value(&mut self, meshes: &mut ResMut<Assets<Mesh>>, val: f32) -> Mesh2dHandle {
self.value += val;
self.value = self.value.clamp(0., self.max_value);
self.regenerate_mesh(meshes)
}
pub fn set_value(&mut self, meshes: &mut ResMut<Assets<Mesh>>, val: f32) -> Mesh2dHandle {
self.value = val;
self.regenerate_mesh(meshes)
}
}