use std::sync::Arc;
use crate::{
align::{anchor_rect, Align, LEFT_TOP},
color,
layers::PaintCmdIdx,
math::{Pos2, Rect, Vec2},
paint::{font, Fonts, PaintCmd, Stroke, TextStyle},
Context, Layer, Srgba,
};
#[derive(Clone)]
pub struct Painter {
ctx: Arc<Context>,
layer: Layer,
clip_rect: Rect,
}
impl Painter {
pub fn new(ctx: Arc<Context>, layer: Layer, clip_rect: Rect) -> Self {
Self {
ctx,
layer,
clip_rect,
}
}
pub fn sub_region(&self, rect: Rect) -> Self {
Self::new(self.ctx.clone(), self.layer, rect.intersect(self.clip_rect))
}
}
impl Painter {
pub(crate) fn ctx(&self) -> &Arc<Context> {
&self.ctx
}
pub(crate) fn fonts(&self) -> &Fonts {
self.ctx.fonts()
}
pub fn layer(&self) -> Layer {
self.layer
}
pub fn clip_rect(&self) -> Rect {
self.clip_rect
}
pub fn set_clip_rect(&mut self, clip_rect: Rect) {
self.clip_rect = clip_rect;
}
pub fn round_to_pixel(&self, point: f32) -> f32 {
self.ctx().round_to_pixel(point)
}
pub fn round_vec_to_pixels(&self, vec: Vec2) -> Vec2 {
self.ctx().round_vec_to_pixels(vec)
}
pub fn round_pos_to_pixels(&self, pos: Pos2) -> Pos2 {
self.ctx().round_pos_to_pixels(pos)
}
}
impl Painter {
pub fn add(&self, paint_cmd: PaintCmd) -> PaintCmdIdx {
self.ctx
.graphics()
.list(self.layer)
.add(self.clip_rect, paint_cmd)
}
pub fn extend(&self, cmds: Vec<PaintCmd>) {
self.ctx
.graphics()
.list(self.layer)
.extend(self.clip_rect, cmds);
}
pub fn set(&self, idx: PaintCmdIdx, cmd: PaintCmd) {
self.ctx
.graphics()
.list(self.layer)
.set(idx, self.clip_rect, cmd)
}
}
impl Painter {
pub fn debug_rect(&mut self, rect: Rect, color: Srgba, text: impl Into<String>) {
self.rect_stroke(rect, 0.0, (1.0, color));
let text_style = TextStyle::Monospace;
self.text(rect.min, LEFT_TOP, text.into(), text_style, color);
}
pub fn error(&self, pos: Pos2, text: impl Into<String>) {
let text = text.into();
let text_style = TextStyle::Monospace;
let font = &self.fonts()[text_style];
let galley = font.layout_multiline(text, f32::INFINITY);
let rect = anchor_rect(Rect::from_min_size(pos, galley.size), LEFT_TOP);
self.add(PaintCmd::Rect {
rect: rect.expand(2.0),
corner_radius: 0.0,
fill: Srgba::black_alpha(240),
stroke: Stroke::new(1.0, color::RED),
});
self.galley(rect.min, galley, text_style, color::RED);
}
pub fn debug_arrow(&self, origin: Pos2, dir: Vec2, stroke: Stroke) {
use crate::math::*;
let full_length = dir.length().at_least(64.0);
let tip_length = full_length / 3.0;
let dir = dir.normalized();
let tip = origin + dir * full_length;
let angle = TAU / 10.0;
self.line_segment([origin, tip], stroke);
self.line_segment(
[
tip,
tip - tip_length * Vec2::angled(angle).rotate_other(dir),
],
stroke,
);
self.line_segment(
[
tip,
tip - tip_length * Vec2::angled(-angle).rotate_other(dir),
],
stroke,
);
}
}
impl Painter {
pub fn line_segment(&self, points: [Pos2; 2], stroke: impl Into<Stroke>) {
self.add(PaintCmd::LineSegment {
points,
stroke: stroke.into(),
});
}
pub fn circle(
&self,
center: Pos2,
radius: f32,
fill_color: impl Into<Srgba>,
stroke: impl Into<Stroke>,
) {
self.add(PaintCmd::Circle {
center,
radius,
fill: fill_color.into(),
stroke: stroke.into(),
});
}
pub fn circle_filled(&self, center: Pos2, radius: f32, fill_color: impl Into<Srgba>) {
self.add(PaintCmd::Circle {
center,
radius,
fill: fill_color.into(),
stroke: Default::default(),
});
}
pub fn circle_stroke(&self, center: Pos2, radius: f32, stroke: impl Into<Stroke>) {
self.add(PaintCmd::Circle {
center,
radius,
fill: Default::default(),
stroke: stroke.into(),
});
}
pub fn rect(
&self,
rect: Rect,
corner_radius: f32,
fill_color: impl Into<Srgba>,
stroke: impl Into<Stroke>,
) {
self.add(PaintCmd::Rect {
rect,
corner_radius,
fill: fill_color.into(),
stroke: stroke.into(),
});
}
pub fn rect_filled(&self, rect: Rect, corner_radius: f32, fill_color: impl Into<Srgba>) {
self.add(PaintCmd::Rect {
rect,
corner_radius,
fill: fill_color.into(),
stroke: Default::default(),
});
}
pub fn rect_stroke(&self, rect: Rect, corner_radius: f32, stroke: impl Into<Stroke>) {
self.add(PaintCmd::Rect {
rect,
corner_radius,
fill: Default::default(),
stroke: stroke.into(),
});
}
}
impl Painter {
pub fn text(
&self,
pos: Pos2,
anchor: (Align, Align),
text: impl Into<String>,
text_style: TextStyle,
text_color: Srgba,
) -> Rect {
let font = &self.fonts()[text_style];
let galley = font.layout_multiline(text.into(), f32::INFINITY);
let rect = anchor_rect(Rect::from_min_size(pos, galley.size), anchor);
self.galley(rect.min, galley, text_style, text_color);
rect
}
pub fn galley(&self, pos: Pos2, galley: font::Galley, text_style: TextStyle, color: Srgba) {
self.add(PaintCmd::Text {
pos,
galley,
text_style,
color,
});
}
}