Migrate SAO UI draw code to script crate
This commit is contained in:
parent
da4fce3d19
commit
77465dab6e
|
@ -7,8 +7,7 @@ edition = "2021"
|
|||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
bitflags = "^1"
|
||||
glam = "^0.21"
|
||||
keyframe = "1"
|
||||
canary_script = { path = "../script", features = ["glam"] }
|
||||
canary_script = { path = "../script" }
|
||||
wee_alloc = "^0.4"
|
||||
|
|
|
@ -1,388 +1 @@
|
|||
use crate::{Color, Vec2};
|
||||
use bitflags::bitflags;
|
||||
use canary_script::{Panel, TextLayout};
|
||||
|
||||
pub enum Corner {
|
||||
TopRight,
|
||||
BottomRight,
|
||||
BottomLeft,
|
||||
TopLeft,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct CornerFlags: u8 {
|
||||
const TOP_RIGHT = 0x01;
|
||||
const BOTTOM_RIGHT = 0x02;
|
||||
const BOTTOM_LEFT = 0x04;
|
||||
const TOP_LEFT = 0x08;
|
||||
const TOP = 0x09;
|
||||
const RIGHT = 0x03;
|
||||
const BOTTOM = 0x06;
|
||||
const LEFT = 0x0C;
|
||||
const ALL = 0x0F;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ColoredTriangle {
|
||||
pub v1: Vec2,
|
||||
pub v2: Vec2,
|
||||
pub v3: Vec2,
|
||||
pub color: Color,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Rect {
|
||||
pub bl: Vec2,
|
||||
pub tr: Vec2,
|
||||
}
|
||||
|
||||
impl Rect {
|
||||
pub fn from_xy_size(xy: Vec2, size: Vec2) -> Self {
|
||||
Self {
|
||||
bl: xy,
|
||||
tr: xy + size,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_circle_bounds(center: Vec2, radius: f32) -> Self {
|
||||
Self {
|
||||
bl: center - radius,
|
||||
tr: center + radius,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_triangle_bounds(tri: &ColoredTriangle) -> Self {
|
||||
Self {
|
||||
bl: tri.v1.min(tri.v2).min(tri.v3),
|
||||
tr: tri.v1.max(tri.v2).max(tri.v3),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inset(&self, d: f32) -> Self {
|
||||
Self {
|
||||
bl: self.bl + d,
|
||||
tr: self.tr - d,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tl(&self) -> Vec2 {
|
||||
Vec2::new(self.bl.x, self.tr.y)
|
||||
}
|
||||
|
||||
pub fn br(&self) -> Vec2 {
|
||||
Vec2::new(self.tr.x, self.bl.y)
|
||||
}
|
||||
|
||||
pub fn offset(&self, offset: Vec2) -> Self {
|
||||
Self {
|
||||
bl: self.bl + offset,
|
||||
tr: self.tr + offset,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scale(&self, scale: f32) -> Self {
|
||||
Self {
|
||||
bl: self.bl * scale,
|
||||
tr: self.tr * scale,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.bl.cmplt(self.tr).all()
|
||||
}
|
||||
|
||||
pub fn intersects_rect(&self, other: &Self) -> bool {
|
||||
self.bl.cmple(other.tr).all() && self.tr.cmpge(other.bl).all()
|
||||
}
|
||||
|
||||
pub fn intersection(&self, other: &Self) -> Option<Self> {
|
||||
let clipped = Self {
|
||||
bl: self.bl.max(other.bl),
|
||||
tr: self.tr.min(other.tr),
|
||||
};
|
||||
|
||||
if clipped.is_valid() {
|
||||
Some(clipped)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn contains_rect(&self, other: &Self) -> bool {
|
||||
self.bl.x < other.bl.x
|
||||
&& self.bl.y < other.bl.y
|
||||
&& self.tr.x > other.tr.x
|
||||
&& self.tr.y > other.tr.y
|
||||
}
|
||||
|
||||
pub fn contains_point(&self, xy: Vec2) -> bool {
|
||||
self.bl.x < xy.x && self.bl.y < xy.y && self.tr.x > xy.x && self.tr.y > xy.y
|
||||
}
|
||||
|
||||
pub fn width(&self) -> f32 {
|
||||
self.tr.x - self.bl.x
|
||||
}
|
||||
|
||||
pub fn height(&self) -> f32 {
|
||||
self.tr.y - self.bl.y
|
||||
}
|
||||
}
|
||||
|
||||
impl From<canary_script::Rect> for Rect {
|
||||
fn from(other: canary_script::Rect) -> Self {
|
||||
Self {
|
||||
bl: other.bl.into(),
|
||||
tr: other.tr.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DrawContext {
|
||||
panel: Panel,
|
||||
offset: Option<Vec2>,
|
||||
clip_rect: Option<Rect>,
|
||||
opacity: Option<f32>,
|
||||
}
|
||||
|
||||
impl DrawContext {
|
||||
pub fn new(panel: Panel) -> Self {
|
||||
Self {
|
||||
panel,
|
||||
offset: None,
|
||||
clip_rect: None,
|
||||
opacity: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw_triangle(&self, v1: Vec2, v2: Vec2, v3: Vec2, color: Color) {
|
||||
if let Some(clip_rect) = self.clip_rect.as_ref() {
|
||||
let tri = ColoredTriangle { v1, v2, v3, color };
|
||||
let bb = Rect::from_triangle_bounds(&tri);
|
||||
|
||||
if clip_rect.contains_rect(&bb) {
|
||||
self.draw_triangle_noclip(tri.v1, tri.v2, tri.v3, tri.color);
|
||||
}
|
||||
} else {
|
||||
self.draw_triangle_noclip(v1, v2, v3, color);
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_triangle_noclip(&self, mut v1: Vec2, mut v2: Vec2, mut v3: Vec2, mut color: Color) {
|
||||
if let Some(offset) = self.offset {
|
||||
v1 += offset;
|
||||
v2 += offset;
|
||||
v3 += offset;
|
||||
}
|
||||
|
||||
if let Some(opacity) = self.opacity.as_ref() {
|
||||
color.a *= opacity;
|
||||
}
|
||||
|
||||
self.panel
|
||||
.draw_triangle(v1.into(), v2.into(), v3.into(), 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, rect: Rect, color: Color) {
|
||||
let rect = if let Some(clip_rect) = self.clip_rect.as_ref() {
|
||||
if let Some(clipped) = clip_rect.intersection(&rect) {
|
||||
clipped
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
rect
|
||||
};
|
||||
|
||||
let v1 = rect.bl;
|
||||
let v2 = Vec2::new(rect.bl.x, rect.tr.y);
|
||||
let v3 = Vec2::new(rect.tr.x, rect.bl.y);
|
||||
let v4 = rect.tr;
|
||||
|
||||
self.draw_triangle_noclip(v1, v2, v3, color);
|
||||
self.draw_triangle_noclip(v2, v3, v4, color);
|
||||
}
|
||||
|
||||
pub fn draw_rounded_rect(&self, rect: Rect, radius: f32, color: Color) {
|
||||
self.draw_partially_rounded_rect(CornerFlags::ALL, rect, radius, color);
|
||||
}
|
||||
|
||||
pub fn draw_partially_rounded_rect(
|
||||
&self,
|
||||
corners: CornerFlags,
|
||||
rect: Rect,
|
||||
radius: f32,
|
||||
color: Color,
|
||||
) {
|
||||
if corners.is_empty() {
|
||||
self.draw_rect(rect, color);
|
||||
return;
|
||||
}
|
||||
|
||||
let mut inner_rect = rect;
|
||||
let inset = rect.inset(radius);
|
||||
|
||||
if corners.intersects(CornerFlags::BOTTOM) {
|
||||
inner_rect.bl.y += radius;
|
||||
|
||||
let mut bottom_edge = Rect {
|
||||
bl: rect.bl,
|
||||
tr: Vec2::new(rect.tr.x, rect.bl.y + radius),
|
||||
};
|
||||
|
||||
if corners.contains(CornerFlags::BOTTOM_LEFT) {
|
||||
bottom_edge.bl.x += radius;
|
||||
self.draw_quarter_circle(Corner::BottomLeft, inset.bl, radius, color);
|
||||
}
|
||||
|
||||
if corners.contains(CornerFlags::BOTTOM_RIGHT) {
|
||||
bottom_edge.tr.x -= radius;
|
||||
self.draw_quarter_circle(Corner::BottomRight, inset.br(), radius, color);
|
||||
}
|
||||
|
||||
self.draw_rect(bottom_edge, color);
|
||||
}
|
||||
|
||||
if corners.intersects(CornerFlags::TOP) {
|
||||
inner_rect.tr.y -= radius;
|
||||
|
||||
let mut top_edge = Rect {
|
||||
bl: Vec2::new(rect.bl.x, rect.tr.y - radius),
|
||||
tr: rect.tr,
|
||||
};
|
||||
|
||||
if corners.contains(CornerFlags::TOP_LEFT) {
|
||||
top_edge.bl.x += radius;
|
||||
self.draw_quarter_circle(Corner::TopLeft, inset.tl(), radius, color);
|
||||
}
|
||||
|
||||
if corners.contains(CornerFlags::TOP_RIGHT) {
|
||||
top_edge.tr.x -= radius;
|
||||
self.draw_quarter_circle(Corner::TopRight, inset.tr, radius, color);
|
||||
}
|
||||
|
||||
self.draw_rect(top_edge, color);
|
||||
}
|
||||
|
||||
self.draw_rect(inner_rect, color);
|
||||
}
|
||||
|
||||
pub fn draw_text_layout(
|
||||
&self,
|
||||
layout: &TextLayout,
|
||||
mut offset: Vec2,
|
||||
scale: f32,
|
||||
mut color: Color,
|
||||
) {
|
||||
if let Some(delta) = self.offset {
|
||||
offset += delta;
|
||||
}
|
||||
|
||||
if let Some(opacity) = self.opacity {
|
||||
color.a *= opacity;
|
||||
}
|
||||
|
||||
self.panel
|
||||
.draw_text_layout(layout, offset.into(), scale, color);
|
||||
}
|
||||
|
||||
pub fn get_clip_rect(&self) -> &Option<Rect> {
|
||||
&self.clip_rect
|
||||
}
|
||||
|
||||
pub fn with_offset(&self, offset: Vec2) -> Self {
|
||||
Self {
|
||||
offset: self.offset.map(|old| old + offset).or(Some(offset)),
|
||||
clip_rect: self.clip_rect.map(|r| r.offset(-offset)),
|
||||
..*self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_clip_rect(&self, mut clip_rect: Rect) -> Option<Self> {
|
||||
if let Some(old) = self.clip_rect {
|
||||
if let Some(clipped) = old.intersection(&clip_rect) {
|
||||
clip_rect = clipped;
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
Some(Self {
|
||||
clip_rect: Some(clip_rect),
|
||||
..*self
|
||||
})
|
||||
}
|
||||
|
||||
pub fn with_opacity(&self, mut opacity: f32) -> Self {
|
||||
if let Some(old) = self.opacity {
|
||||
opacity *= old;
|
||||
}
|
||||
|
||||
Self {
|
||||
opacity: Some(opacity),
|
||||
..*self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ impl PanelImpl for DummyPanel {
|
|||
}
|
||||
|
||||
fn draw(&mut self) {
|
||||
let ctx = draw::DrawContext::new(self.panel);
|
||||
let ctx = canary_script::draw::DrawContext::new(self.panel);
|
||||
self.menu.draw(&ctx);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::anim::Animation;
|
||||
use crate::draw::{CornerFlags, DrawContext, Rect};
|
||||
use crate::{Color, CursorEventKind, Vec2};
|
||||
use canary_script::draw::{CornerFlags, DrawContext, Rect};
|
||||
use canary_script::{Font, TextLayout};
|
||||
use keyframe::functions::*;
|
||||
|
||||
|
@ -11,10 +11,10 @@ pub mod shell;
|
|||
pub mod text;
|
||||
|
||||
use button::{Button, RectButton, RoundButton};
|
||||
use menu::{SlotMenu, SlotMenuEvent, TabMenu};
|
||||
use scroll::{ScrollBar, ScrollView};
|
||||
use shell::{Offset, Reveal};
|
||||
use text::{HorizontalAlignment, Label, LabelText};
|
||||
use menu::{SlotMenu, SlotMenuEvent, TabMenu};
|
||||
|
||||
pub trait Widget {
|
||||
fn update(&mut self, dt: f32) {}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use super::Widget;
|
||||
use crate::anim::Animation;
|
||||
use crate::draw::DrawContext;
|
||||
use crate::{CursorEventKind, Vec2};
|
||||
use canary_script::draw::DrawContext;
|
||||
use keyframe::functions::*;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
canary_types = { path = "../types" }
|
||||
|
||||
[features]
|
||||
glam = ["canary_types/glam"]
|
||||
bitflags = "^1"
|
||||
canary_types = { path = "../types", features = ["glam"] }
|
||||
glam = "^0.21"
|
||||
|
|
|
@ -0,0 +1,388 @@
|
|||
use super::{Color, TextLayout, Panel};
|
||||
use bitflags::bitflags;
|
||||
use glam::Vec2;
|
||||
|
||||
pub enum Corner {
|
||||
TopRight,
|
||||
BottomRight,
|
||||
BottomLeft,
|
||||
TopLeft,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct CornerFlags: u8 {
|
||||
const TOP_RIGHT = 0x01;
|
||||
const BOTTOM_RIGHT = 0x02;
|
||||
const BOTTOM_LEFT = 0x04;
|
||||
const TOP_LEFT = 0x08;
|
||||
const TOP = 0x09;
|
||||
const RIGHT = 0x03;
|
||||
const BOTTOM = 0x06;
|
||||
const LEFT = 0x0C;
|
||||
const ALL = 0x0F;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ColoredTriangle {
|
||||
pub v1: Vec2,
|
||||
pub v2: Vec2,
|
||||
pub v3: Vec2,
|
||||
pub color: Color,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Rect {
|
||||
pub bl: Vec2,
|
||||
pub tr: Vec2,
|
||||
}
|
||||
|
||||
impl Rect {
|
||||
pub fn from_xy_size(xy: Vec2, size: Vec2) -> Self {
|
||||
Self {
|
||||
bl: xy,
|
||||
tr: xy + size,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_circle_bounds(center: Vec2, radius: f32) -> Self {
|
||||
Self {
|
||||
bl: center - radius,
|
||||
tr: center + radius,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_triangle_bounds(tri: &ColoredTriangle) -> Self {
|
||||
Self {
|
||||
bl: tri.v1.min(tri.v2).min(tri.v3),
|
||||
tr: tri.v1.max(tri.v2).max(tri.v3),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inset(&self, d: f32) -> Self {
|
||||
Self {
|
||||
bl: self.bl + d,
|
||||
tr: self.tr - d,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tl(&self) -> Vec2 {
|
||||
Vec2::new(self.bl.x, self.tr.y)
|
||||
}
|
||||
|
||||
pub fn br(&self) -> Vec2 {
|
||||
Vec2::new(self.tr.x, self.bl.y)
|
||||
}
|
||||
|
||||
pub fn offset(&self, offset: Vec2) -> Self {
|
||||
Self {
|
||||
bl: self.bl + offset,
|
||||
tr: self.tr + offset,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scale(&self, scale: f32) -> Self {
|
||||
Self {
|
||||
bl: self.bl * scale,
|
||||
tr: self.tr * scale,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.bl.cmplt(self.tr).all()
|
||||
}
|
||||
|
||||
pub fn intersects_rect(&self, other: &Self) -> bool {
|
||||
self.bl.cmple(other.tr).all() && self.tr.cmpge(other.bl).all()
|
||||
}
|
||||
|
||||
pub fn intersection(&self, other: &Self) -> Option<Self> {
|
||||
let clipped = Self {
|
||||
bl: self.bl.max(other.bl),
|
||||
tr: self.tr.min(other.tr),
|
||||
};
|
||||
|
||||
if clipped.is_valid() {
|
||||
Some(clipped)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn contains_rect(&self, other: &Self) -> bool {
|
||||
self.bl.x < other.bl.x
|
||||
&& self.bl.y < other.bl.y
|
||||
&& self.tr.x > other.tr.x
|
||||
&& self.tr.y > other.tr.y
|
||||
}
|
||||
|
||||
pub fn contains_point(&self, xy: Vec2) -> bool {
|
||||
self.bl.x < xy.x && self.bl.y < xy.y && self.tr.x > xy.x && self.tr.y > xy.y
|
||||
}
|
||||
|
||||
pub fn width(&self) -> f32 {
|
||||
self.tr.x - self.bl.x
|
||||
}
|
||||
|
||||
pub fn height(&self) -> f32 {
|
||||
self.tr.y - self.bl.y
|
||||
}
|
||||
}
|
||||
|
||||
impl From<canary_types::Rect> for Rect {
|
||||
fn from(other: canary_types::Rect) -> Self {
|
||||
Self {
|
||||
bl: other.bl.into(),
|
||||
tr: other.tr.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DrawContext {
|
||||
panel: Panel,
|
||||
offset: Option<Vec2>,
|
||||
clip_rect: Option<Rect>,
|
||||
opacity: Option<f32>,
|
||||
}
|
||||
|
||||
impl DrawContext {
|
||||
pub fn new(panel: Panel) -> Self {
|
||||
Self {
|
||||
panel,
|
||||
offset: None,
|
||||
clip_rect: None,
|
||||
opacity: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw_triangle(&self, v1: Vec2, v2: Vec2, v3: Vec2, color: Color) {
|
||||
if let Some(clip_rect) = self.clip_rect.as_ref() {
|
||||
let tri = ColoredTriangle { v1, v2, v3, color };
|
||||
let bb = Rect::from_triangle_bounds(&tri);
|
||||
|
||||
if clip_rect.contains_rect(&bb) {
|
||||
self.draw_triangle_noclip(tri.v1, tri.v2, tri.v3, tri.color);
|
||||
}
|
||||
} else {
|
||||
self.draw_triangle_noclip(v1, v2, v3, color);
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_triangle_noclip(&self, mut v1: Vec2, mut v2: Vec2, mut v3: Vec2, mut color: Color) {
|
||||
if let Some(offset) = self.offset {
|
||||
v1 += offset;
|
||||
v2 += offset;
|
||||
v3 += offset;
|
||||
}
|
||||
|
||||
if let Some(opacity) = self.opacity.as_ref() {
|
||||
color.a *= opacity;
|
||||
}
|
||||
|
||||
self.panel
|
||||
.draw_triangle(v1.into(), v2.into(), v3.into(), 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, rect: Rect, color: Color) {
|
||||
let rect = if let Some(clip_rect) = self.clip_rect.as_ref() {
|
||||
if let Some(clipped) = clip_rect.intersection(&rect) {
|
||||
clipped
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
rect
|
||||
};
|
||||
|
||||
let v1 = rect.bl;
|
||||
let v2 = Vec2::new(rect.bl.x, rect.tr.y);
|
||||
let v3 = Vec2::new(rect.tr.x, rect.bl.y);
|
||||
let v4 = rect.tr;
|
||||
|
||||
self.draw_triangle_noclip(v1, v2, v3, color);
|
||||
self.draw_triangle_noclip(v2, v3, v4, color);
|
||||
}
|
||||
|
||||
pub fn draw_rounded_rect(&self, rect: Rect, radius: f32, color: Color) {
|
||||
self.draw_partially_rounded_rect(CornerFlags::ALL, rect, radius, color);
|
||||
}
|
||||
|
||||
pub fn draw_partially_rounded_rect(
|
||||
&self,
|
||||
corners: CornerFlags,
|
||||
rect: Rect,
|
||||
radius: f32,
|
||||
color: Color,
|
||||
) {
|
||||
if corners.is_empty() {
|
||||
self.draw_rect(rect, color);
|
||||
return;
|
||||
}
|
||||
|
||||
let mut inner_rect = rect;
|
||||
let inset = rect.inset(radius);
|
||||
|
||||
if corners.intersects(CornerFlags::BOTTOM) {
|
||||
inner_rect.bl.y += radius;
|
||||
|
||||
let mut bottom_edge = Rect {
|
||||
bl: rect.bl,
|
||||
tr: Vec2::new(rect.tr.x, rect.bl.y + radius),
|
||||
};
|
||||
|
||||
if corners.contains(CornerFlags::BOTTOM_LEFT) {
|
||||
bottom_edge.bl.x += radius;
|
||||
self.draw_quarter_circle(Corner::BottomLeft, inset.bl, radius, color);
|
||||
}
|
||||
|
||||
if corners.contains(CornerFlags::BOTTOM_RIGHT) {
|
||||
bottom_edge.tr.x -= radius;
|
||||
self.draw_quarter_circle(Corner::BottomRight, inset.br(), radius, color);
|
||||
}
|
||||
|
||||
self.draw_rect(bottom_edge, color);
|
||||
}
|
||||
|
||||
if corners.intersects(CornerFlags::TOP) {
|
||||
inner_rect.tr.y -= radius;
|
||||
|
||||
let mut top_edge = Rect {
|
||||
bl: Vec2::new(rect.bl.x, rect.tr.y - radius),
|
||||
tr: rect.tr,
|
||||
};
|
||||
|
||||
if corners.contains(CornerFlags::TOP_LEFT) {
|
||||
top_edge.bl.x += radius;
|
||||
self.draw_quarter_circle(Corner::TopLeft, inset.tl(), radius, color);
|
||||
}
|
||||
|
||||
if corners.contains(CornerFlags::TOP_RIGHT) {
|
||||
top_edge.tr.x -= radius;
|
||||
self.draw_quarter_circle(Corner::TopRight, inset.tr, radius, color);
|
||||
}
|
||||
|
||||
self.draw_rect(top_edge, color);
|
||||
}
|
||||
|
||||
self.draw_rect(inner_rect, color);
|
||||
}
|
||||
|
||||
pub fn draw_text_layout(
|
||||
&self,
|
||||
layout: &TextLayout,
|
||||
mut offset: Vec2,
|
||||
scale: f32,
|
||||
mut color: Color,
|
||||
) {
|
||||
if let Some(delta) = self.offset {
|
||||
offset += delta;
|
||||
}
|
||||
|
||||
if let Some(opacity) = self.opacity {
|
||||
color.a *= opacity;
|
||||
}
|
||||
|
||||
self.panel
|
||||
.draw_text_layout(layout, offset.into(), scale, color);
|
||||
}
|
||||
|
||||
pub fn get_clip_rect(&self) -> &Option<Rect> {
|
||||
&self.clip_rect
|
||||
}
|
||||
|
||||
pub fn with_offset(&self, offset: Vec2) -> Self {
|
||||
Self {
|
||||
offset: self.offset.map(|old| old + offset).or(Some(offset)),
|
||||
clip_rect: self.clip_rect.map(|r| r.offset(-offset)),
|
||||
..*self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_clip_rect(&self, mut clip_rect: Rect) -> Option<Self> {
|
||||
if let Some(old) = self.clip_rect {
|
||||
if let Some(clipped) = old.intersection(&clip_rect) {
|
||||
clip_rect = clipped;
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
Some(Self {
|
||||
clip_rect: Some(clip_rect),
|
||||
..*self
|
||||
})
|
||||
}
|
||||
|
||||
pub fn with_opacity(&self, mut opacity: f32) -> Self {
|
||||
if let Some(old) = self.opacity {
|
||||
opacity *= old;
|
||||
}
|
||||
|
||||
Self {
|
||||
opacity: Some(opacity),
|
||||
..*self
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
pub use canary_types::*;
|
||||
|
||||
pub mod draw;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! export_abi {
|
||||
($panel_impl: ident) => {
|
||||
|
|
Loading…
Reference in New Issue