#[cfg(feature = "wgpu")]
use bytemuck::{Pod, Zeroable};
pub mod shape {
pub const CIRCLE: u32 = 0;
pub const RING: u32 = 1;
pub const SQUARE: u32 = 2;
pub const TRIANGLE: u32 = 3;
pub const DIAMOND: u32 = 4;
}
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "wgpu", derive(Pod, Zeroable))]
pub struct QuadInstance {
pub center: [f32; 2],
pub radius: f32,
pub inner: f32,
pub color: [f32; 4],
pub aa: f32,
pub shape: u32,
pub _pad: [f32; 2],
}
impl QuadInstance {
#[inline]
pub fn half_extent(&self) -> f32 {
self.radius + self.aa
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct CircleInstance {
pub center: [f32; 2],
pub radius: f32,
pub color: [f32; 4],
pub aa: f32,
}
impl CircleInstance {
pub fn lower(self) -> QuadInstance {
QuadInstance {
center: self.center,
radius: self.radius,
inner: 0.0,
color: self.color,
aa: self.aa,
shape: shape::CIRCLE,
_pad: [0.0, 0.0],
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct RingInstance {
pub center: [f32; 2],
pub radius: f32,
pub inner: f32,
pub color: [f32; 4],
pub aa: f32,
}
impl RingInstance {
pub fn lower(self) -> QuadInstance {
QuadInstance {
center: self.center,
radius: self.radius,
inner: self.inner.clamp(0.0, self.radius),
color: self.color,
aa: self.aa,
shape: shape::RING,
_pad: [0.0, 0.0],
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct MarkerInstance {
pub center: [f32; 2],
pub radius: f32,
pub corner: f32,
pub color: [f32; 4],
pub aa: f32,
pub shape: u32,
}
impl MarkerInstance {
pub fn lower(self) -> QuadInstance {
QuadInstance {
center: self.center,
radius: self.radius,
inner: self.corner,
color: self.color,
aa: self.aa,
shape: self.shape,
_pad: [0.0, 0.0],
}
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "wgpu", derive(Pod, Zeroable))]
pub struct LineInstance {
pub a: [f32; 2],
pub b: [f32; 2],
pub half_width: f32,
pub aa: f32,
pub cap: u32,
pub _pad0: u32,
pub color: [f32; 4],
}
pub mod cap {
pub const BUTT: u32 = 0;
pub const ROUND: u32 = 1;
}
impl LineInstance {
pub fn round(a: [f32; 2], b: [f32; 2], half_width: f32, aa: f32, color: [f32; 4]) -> Self {
Self { a, b, half_width, aa, cap: cap::ROUND, _pad0: 0, color }
}
pub fn butt(a: [f32; 2], b: [f32; 2], half_width: f32, aa: f32, color: [f32; 4]) -> Self {
Self { a, b, half_width, aa, cap: cap::BUTT, _pad0: 0, color }
}
#[inline]
pub fn bounds(&self) -> (f32, f32, f32, f32) {
let r = self.half_width + self.aa;
let min_x = self.a[0].min(self.b[0]) - r;
let max_x = self.a[0].max(self.b[0]) + r;
let min_y = self.a[1].min(self.b[1]) - r;
let max_y = self.a[1].max(self.b[1]) + r;
(min_x, min_y, max_x, max_y)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn typed_primitives_lower_to_the_right_quad_shape() {
let c = CircleInstance { center: [10.0, 20.0], radius: 5.0, color: [1.0, 0.0, 0.0, 1.0], aa: 1.0 }
.lower();
assert_eq!(c.shape, shape::CIRCLE);
assert_eq!(c.inner, 0.0);
assert_eq!(c.center, [10.0, 20.0]);
assert_eq!(c.half_extent(), 6.0);
let r = RingInstance { center: [0.0, 0.0], radius: 8.0, inner: 4.0, color: [0.0; 4], aa: 1.5 }
.lower();
assert_eq!(r.shape, shape::RING);
assert_eq!(r.inner, 4.0);
let bad = RingInstance { center: [0.0, 0.0], radius: 3.0, inner: 9.0, color: [0.0; 4], aa: 1.0 }
.lower();
assert_eq!(bad.inner, 3.0, "inner clamped to radius");
let m = MarkerInstance {
center: [1.0, 2.0],
radius: 6.0,
corner: 1.0,
color: [0.0; 4],
aa: 1.0,
shape: shape::DIAMOND,
}
.lower();
assert_eq!(m.shape, shape::DIAMOND);
assert_eq!(m.inner, 1.0, "corner carried in inner");
}
#[test]
fn line_bounds_inflate_by_halfwidth_plus_aa() {
let l = LineInstance::round([10.0, 10.0], [30.0, 14.0], 3.0, 1.0, [1.0; 4]);
let (mnx, mny, mxx, mxy) = l.bounds();
assert_eq!(mnx, 10.0 - 4.0);
assert_eq!(mny, 10.0 - 4.0);
assert_eq!(mxx, 30.0 + 4.0);
assert_eq!(mxy, 14.0 + 4.0);
assert_eq!(l.cap, cap::ROUND);
assert_eq!(LineInstance::butt([0.0; 2], [1.0, 0.0], 1.0, 0.0, [0.0; 4]).cap, cap::BUTT);
}
}