1use crate::frame::Frame;
2use crate::graphics::Graphics;
3use crate::mesh::{MeshId, MeshInstance};
4use crate::primitives::CircleInstance;
5use crate::sprite::{BlendMode, SpriteInstance};
6use crate::texture::TextureId;
7use crate::transform::Mat4;
8
9pub struct Painter<'a> {
10 frame: &'a mut Frame,
11 gfx: &'a mut Graphics,
12}
13
14impl<'a> Painter<'a> {
15 pub(crate) fn new(frame: &'a mut Frame, gfx: &'a mut Graphics) -> Self {
16 Self { frame, gfx }
17 }
18
19 pub fn clear(&mut self, color: [f32; 4]) {
20 self.frame.clear_color = color;
21 }
22
23 pub fn visible_rect(&self) -> ([f32; 2], [f32; 2]) {
25 self.gfx.camera.visible_rect()
26 }
27
28 pub fn sprite(&mut self, tex: TextureId, pos: [f32; 2], size: [f32; 2]) {
29 self.gfx
30 .sprites
31 .draw(tex, 0, BlendMode::Alpha, SpriteInstance::at(pos, size));
32 }
33
34 pub fn sprite_ex(
35 &mut self,
36 tex: TextureId,
37 inst: SpriteInstance,
38 layer: i16,
39 blend: BlendMode,
40 ) {
41 self.gfx.sprites.draw(tex, layer, blend, inst);
42 }
43
44 pub fn rect(&mut self, pos: [f32; 2], size: [f32; 2], color: [f32; 4]) {
46 let tex = self.gfx.white_texture();
47 self.gfx.sprites.draw(
48 tex,
49 0,
50 BlendMode::Alpha,
51 SpriteInstance::at(pos, size).with_color(color),
52 );
53 }
54
55 pub fn rect_outline(&mut self, pos: [f32; 2], size: [f32; 2], thickness: f32, color: [f32; 4]) {
57 let [x, y] = pos;
58 let [w, h] = size;
59 self.line([x, y], [x + w, y], thickness, color);
60 self.line([x + w, y], [x + w, y + h], thickness, color);
61 self.line([x + w, y + h], [x, y + h], thickness, color);
62 self.line([x, y + h], [x, y], thickness, color);
63 }
64
65 pub fn line(&mut self, a: [f32; 2], b: [f32; 2], thickness: f32, color: [f32; 4]) {
67 let dx = b[0] - a[0];
68 let dy = b[1] - a[1];
69 let len = (dx * dx + dy * dy).sqrt();
70 if len < 1e-4 {
71 return;
72 }
73 let angle = dy.atan2(dx);
74 let half_t = thickness * 0.5;
75 let cx = (a[0] + b[0]) * 0.5;
79 let cy = (a[1] + b[1]) * 0.5;
80 let tex = self.gfx.white_texture();
81 let inst = SpriteInstance {
82 pos: [cx - len * 0.5, cy - half_t],
83 size: [len, thickness],
84 uv_min: [0.0, 0.0],
85 uv_max: [1.0, 1.0],
86 color,
87 rotation: angle,
88 _pad: [0.0; 3],
89 };
90 self.gfx.sprites.draw(tex, 0, BlendMode::Alpha, inst);
91 }
92
93 pub fn circle(&mut self, center: [f32; 2], radius: f32, color: [f32; 4]) {
95 self.circle_ex(center, radius, 0.0, color, 0);
96 }
97
98 pub fn circle_outline(
100 &mut self,
101 center: [f32; 2],
102 radius: f32,
103 thickness: f32,
104 color: [f32; 4],
105 ) {
106 self.circle_ex(center, radius, thickness.max(1.0), color, 0);
107 }
108
109 pub fn circle_ex(
112 &mut self,
113 center: [f32; 2],
114 radius: f32,
115 thickness: f32,
116 color: [f32; 4],
117 layer: i16,
118 ) {
119 self.gfx.primitives.push(
120 layer,
121 CircleInstance {
122 center,
123 radius,
124 thickness,
125 color,
126 },
127 );
128 }
129
130 pub fn text(&mut self, pos: [f32; 2], s: &str, size_px: f32, color: [f32; 4]) {
132 self.gfx.text.queue(s, pos, size_px, color);
133 }
134
135 pub fn mesh(&mut self, mesh: MeshId, model: Mat4, color: [f32; 4]) {
139 self.gfx.draw_mesh(mesh, MeshInstance::new(model, color));
140 }
141
142 #[cfg(feature = "vector")]
145 pub fn vector<F: FnOnce(&mut vello::Scene)>(&mut self, f: F) {
146 f(self.gfx.vector.scene());
147 }
148}
149
150impl<'a> Drop for Painter<'a> {
151 fn drop(&mut self) {
152 self.gfx.flush_into(self.frame);
153 }
154}