Files
IntroToWebAuthoring/src/render/renderer.rs
T
2025-10-23 11:25:02 -06:00

162 lines
5.1 KiB
Rust

use wasm_bindgen::{Clamped, prelude::*};
use web_sys::ImageData;
use crate::render::{RESOLUTION, buffer::ImgBuffer, calc_resolution, rand::Rnd};
#[wasm_bindgen]
pub struct Renderer {
pub(crate) ctx: web_sys::CanvasRenderingContext2d,
pub(crate) img: ImgBuffer,
pub(crate) rand: Rnd,
pub canvas_width: u32,
pub canvas_height: u32,
pub actual_width: u32,
pub actual_height: u32,
pub distortion_x: f32,
pub distortion_y: f32,
}
impl Renderer {
pub fn new(canvas: &web_sys::HtmlCanvasElement, width: u32, height: u32) -> Self {
let (cwidth, cheight, dist_x, dist_y) = calc_resolution(width, height);
// let ctx = canvas.get_context("2d").unwrap().unwrap();
let img = ImgBuffer::new(cwidth, cheight);
let rand = Rnd::new(12345);
Self {
ctx: canvas
.get_context("2d")
.unwrap()
.unwrap()
.dyn_into::<web_sys::CanvasRenderingContext2d>()
.unwrap(),
img,
rand,
canvas_width: cwidth,
canvas_height: cheight,
actual_width: width,
actual_height: height,
distortion_x: dist_x,
distortion_y: dist_y,
}
}
pub fn resize(&mut self, width: u32, height: u32) {
let (cwidth, cheight, dist_x, dist_y) = calc_resolution(width, height);
self.canvas_width = cwidth;
self.canvas_height = cheight;
self.actual_width = width;
self.actual_height = height;
self.distortion_x = dist_x;
self.distortion_y = dist_y;
self.img.resize(cwidth, cheight);
self.ctx.canvas().unwrap().set_width(cwidth);
self.ctx.canvas().unwrap().set_height(cheight);
}
pub fn update(&mut self) {
let data = ImageData::new_with_u8_clamped_array_and_sh(
Clamped(&self.img.data),
self.img.width(),
self.img.height(),
)
.unwrap();
self.ctx.put_image_data(&data, 0., 0.).unwrap();
}
pub fn clear(&mut self) {
self.img.data = vec![0; (self.img.width() * self.img.height() * 4) as usize];
}
}
impl Renderer {
pub fn undistort(&self, x: f32, y: f32) -> (f32, f32) {
(
x * (self.canvas_width as f32 / self.actual_width as f32),
y * (self.canvas_height as f32 / self.actual_height as f32),
)
}
/// Draw a rectangle centered at (cx, cy) with the given width and height.
pub fn rect_center_xywh(
&mut self,
cx: i32,
cy: i32,
width: i32,
height: i32,
color: (u8, u8, u8),
) {
self.rect_xyxy(
cx - width / 2,
cy - height / 2,
cx + width / 2,
cy + height / 2,
color,
);
}
/// Draw a rectangle at (x, y) with the given width and height.
pub fn rect_xywh(&mut self, x: i32, y: i32, width: i32, height: i32, color: (u8, u8, u8)) {
self.rect_xyxy(x, y, x + width, y + height, color);
}
/// Draw a rectangle at (x1, y1) with the given width and height.
pub fn rect_xyxy(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: (u8, u8, u8)) {
let (leftx, topy) = self.undistort(x1 as f32, y1 as f32);
let (rightx, bottomy) = self.undistort(x2 as f32, y2 as f32);
let leftx = leftx.max(0f32);
let topy = topy.max(0f32);
let rightx = rightx.min(self.canvas_width as f32);
let bottomy = bottomy.min(self.canvas_height as f32);
for x in leftx as i32..rightx as i32 {
for y in topy as i32..bottomy as i32 {
self.img.set_pixel(x as u32, y as u32, color);
}
}
}
/// Calculate the square distance between two points on the screen, and convert to canvas coordinates for processing
pub fn screen_dist_sq(&self, x: f32, y: f32, rx: f32, ry: f32) -> f32 {
(rx - x).powf(2.) * self.distortion_y + (ry - y).powf(2.) * self.distortion_x
}
pub fn circle(&mut self, cx: i32, cy: i32, radius: i32, color: (u8, u8, u8)) {
let radius = radius as f32;
let (leftx, topy) = self.undistort(cx as f32 - radius, cy as f32 - radius);
let (rightx, bottomy) = self.undistort(cx as f32 + radius, cy as f32 + radius);
let (cx, cy) = self.undistort(cx as f32, cy as f32);
let leftx = leftx.max(0f32);
let topy = topy.max(0f32);
let rightx = rightx.min(self.canvas_width as f32);
let bottomy = bottomy.min(self.canvas_height as f32);
// let e = (self.canvas_height as f32 / self.actual_height as f32)
// * (self.canvas_width as f32 / self.actual_width as f32);
let e = RESOLUTION.pow(2) as f32 / (self.actual_width * self.actual_height) as f32;
let r2 = (radius).powf(2.) * e;
// log!("{}, {}, {}, {}", leftx, rightx, topy, bottomy);
for x in leftx as i32..rightx as i32 {
for y in topy as i32..bottomy as i32 {
if self.screen_dist_sq(x as f32, y as f32, cx, cy) <= r2 {
self.img.set_pixel(x as u32, y as u32, color);
}
}
}
}
}