use crate::{Framebuffer, Lights, color, lights_plane::LIGHT_LOCATIONS};
pub struct RectangleBuilder {
color: color::RGB,
bottom_left: (f32, f32),
width: f32,
height: f32,
mask: Lights,
fall_off: Option<f32>,
}
impl RectangleBuilder {
pub fn new(color: color::RGB, bottom_left: (f32, f32), width: f32, height: f32) -> Self {
Self {
color,
bottom_left,
width,
height,
mask: Lights::all(),
fall_off: None,
}
}
#[must_use]
pub fn mask(mut self, mask: Lights) -> Self {
self.mask = mask;
self
}
#[must_use]
pub fn fall_off(mut self, distance: f32) -> Self {
self.fall_off = Some(distance);
self
}
pub fn draw(self, fb: &mut Framebuffer) {
for idx in self.mask.indices() {
let light_pos = LIGHT_LOCATIONS[idx];
let alpha = if let Some(fall_off_distance) = self.fall_off {
let inner_dist =
distance_within_rectangle(light_pos, self.bottom_left, self.width, self.height);
(inner_dist / fall_off_distance).clamp(0., 1.)
} else if point_within_rectangle(light_pos, self.bottom_left, self.width, self.height) {
1.0
} else {
0.0
};
if alpha > 0.0 {
fb.set_color(Lights::from_index(idx), color::dim_to(self.color, alpha));
}
}
}
}
fn point_within_rectangle(
p: (f32, f32),
bottom_left: (f32, f32),
width: f32,
height: f32,
) -> bool {
let (p_x, p_y) = p;
let (rect_x, rect_y) = bottom_left;
p_x > rect_x && p_x < rect_x + width && p_y > rect_y && p_y < rect_y + height
}
fn distance_within_rectangle(
p: (f32, f32),
bottom_left: (f32, f32),
width: f32,
height: f32,
) -> f32 {
if !point_within_rectangle(p, bottom_left, width, height) {
return 0.0;
}
let (p_x, p_y) = p;
let (rect_x, rect_y) = bottom_left;
f32::min(
f32::min((p_y - rect_y - height).abs(), (p_y - rect_y).abs()),
f32::min((p_x - rect_x - width).abs(), (p_x - rect_x).abs()),
)
}