sao-ui-rs/src/draw.rs

157 lines
4.9 KiB
Rust

use crate::abi::UiPanel;
use crate::{Color, Vec2};
pub enum Corner {
TopRight,
BottomRight,
BottomLeft,
TopLeft,
}
pub struct DrawContext {
panel: UiPanel,
offset: Vec2,
opacity: Option<f32>,
}
impl DrawContext {
pub fn new(panel: UiPanel) -> Self {
Self {
panel,
offset: Vec2::ZERO,
opacity: None,
}
}
pub fn draw_triangle(&self, v1: Vec2, v2: Vec2, v3: Vec2, mut color: Color) {
let v1 = v1 + self.offset;
let v2 = v2 + self.offset;
let v3 = v3 + self.offset;
if let Some(opacity) = self.opacity.as_ref() {
color.a *= opacity;
}
self.panel.draw_triangle(v1, v2, v3, color);
}
pub fn draw_circle(&self, center: Vec2, radius: f32, color: Color) {
use std::f32::consts::PI;
let delta = PI / 16.0;
let limit = PI * 2.0 + delta;
let mut last_spoke = Vec2::new(radius + center.x, center.y);
let mut theta = delta;
while theta < limit {
let new_spoke = Vec2::from_angle(theta) * radius + center;
self.draw_triangle(center, last_spoke, new_spoke, color);
last_spoke = new_spoke;
theta += delta;
}
}
pub fn draw_quarter_circle(&self, corner: Corner, center: Vec2, radius: f32, color: Color) {
use std::f32::consts::{FRAC_PI_2, PI};
let spoke_num = 16.0;
let delta = PI / 4.0 / spoke_num;
let (mut theta, limit) = match corner {
Corner::TopRight => (0.0, FRAC_PI_2),
Corner::BottomRight => (FRAC_PI_2 * 3.0, PI * 2.0),
Corner::BottomLeft => (PI, FRAC_PI_2 * 3.0),
Corner::TopLeft => (FRAC_PI_2, PI),
};
let mut last_spoke = Vec2::from_angle(theta) * radius + center;
while theta < limit {
theta += delta;
let new_spoke = Vec2::from_angle(theta) * radius + center;
self.draw_triangle(center, last_spoke, new_spoke, color);
last_spoke = new_spoke;
}
}
pub fn draw_ring(&self, center: Vec2, radius: f32, thickness: f32, color: Color) {
use std::f32::consts::PI;
let delta = PI / 64.0;
let limit = PI * 2.0 + delta;
let mut last_spoke = glam::Vec2::new(radius + center.x, center.y);
let mut last_theta = 0.0;
let mut theta = delta;
while theta < limit {
let angle = Vec2::from_angle(theta);
let new_spoke = angle * radius + center;
let new_spoke2 = angle * (radius + thickness) + center;
let last_spoke2 = Vec2::from_angle(last_theta) * (radius + thickness) + center;
self.draw_triangle(new_spoke2, last_spoke, new_spoke, color);
self.draw_triangle(new_spoke2, last_spoke2, last_spoke, color);
last_spoke = new_spoke;
last_theta = theta;
theta += delta;
}
}
pub fn draw_rect(&self, xy: Vec2, size: Vec2, color: Color) {
let v1 = xy;
let v2 = v1 + Vec2::new(size.x, 0.0);
let v3 = v1 + Vec2::new(0.0, size.y);
let v4 = v1 + size;
self.draw_triangle(v1, v2, v3, color);
self.draw_triangle(v2, v3, v4, color);
}
pub fn draw_rounded_rect(&self, xy: Vec2, size: Vec2, radius: f32, color: Color) {
let xoff = Vec2::new(radius, 0.0);
let yoff = Vec2::new(0.0, radius);
let sizex = Vec2::new(size.x, 0.0);
let sizey = Vec2::new(0.0, size.y);
let inner_size = size - Vec2::new(radius, radius) * 2.0;
let lr_edge_size = Vec2::new(radius, inner_size.y);
let tb_edge_size = Vec2::new(inner_size.x, radius);
self.draw_rect(xy + yoff, lr_edge_size, color); // left edge
self.draw_rect(xy + yoff + sizex - xoff, lr_edge_size, color); // right edge
self.draw_rect(xy + xoff, tb_edge_size, color); // bottom edge
self.draw_rect(xy + xoff + sizey - yoff, tb_edge_size, color); // top edge
let tr = xy + size - xoff - yoff;
let br = xy + sizex - xoff + yoff;
let bl = xy + xoff + yoff;
let tl = xy + sizey + xoff - yoff;
self.draw_quarter_circle(Corner::TopRight, tr, radius, color);
self.draw_quarter_circle(Corner::BottomRight, br, radius, color);
self.draw_quarter_circle(Corner::BottomLeft, bl, radius, color);
self.draw_quarter_circle(Corner::TopLeft, tl, radius, color);
self.draw_rect(bl, inner_size, color);
}
pub fn with_offset(&self, offset: Vec2) -> Self {
Self {
offset: self.offset + offset,
..*self
}
}
pub fn with_opacity(&self, opacity: f32) -> Self {
let opacity = if let Some(old) = self.opacity.as_ref() {
opacity * old
} else {
opacity
};
Self {
opacity: Some(opacity),
..*self
}
}
}