1use jag_draw::{Brush, FillRule, Path, PathCmd, Rect, RoundedRect};
2
3use crate::canvas::Canvas;
4
5#[derive(Clone, Copy, Debug, Default)]
6pub struct BorderWidths {
7 pub top: f32,
8 pub right: f32,
9 pub bottom: f32,
10 pub left: f32,
11}
12
13#[derive(Clone, Debug)]
14pub struct BorderStyle {
15 pub widths: BorderWidths,
16 pub brush: Brush,
17}
18
19#[derive(Clone, Debug, Default)]
20pub struct RectStyle {
21 pub fill: Option<Brush>,
22 pub border: Option<BorderStyle>,
23}
24
25fn snapped_rect(canvas: &Canvas, x: f32, y: f32, w: f32, h: f32) -> (f32, f32, f32, f32) {
26 let snapped = canvas.snap_rect_logical_to_device(Rect { x, y, w, h });
27 (snapped.x, snapped.y, snapped.w, snapped.h)
28}
29
30pub fn snap_rect_to_device(canvas: &Canvas, rect: Rect) -> Rect {
32 canvas.snap_rect_logical_to_device(rect)
33}
34
35pub fn draw_snapped_rectangle(
38 canvas: &mut Canvas,
39 x: f32,
40 y: f32,
41 w: f32,
42 h: f32,
43 style: &RectStyle,
44 z: i32,
45) {
46 let (x, y, w, h) = snapped_rect(canvas, x, y, w, h);
47 draw_rectangle(canvas, x, y, w, h, style, z);
48}
49
50pub fn draw_rectangle(
52 canvas: &mut Canvas,
53 x: f32,
54 y: f32,
55 w: f32,
56 h: f32,
57 style: &RectStyle,
58 z: i32,
59) {
60 if let Some(fill) = &style.fill {
61 canvas.fill_rect(x, y, w, h, fill.clone(), z);
62 }
63 if let Some(border) = &style.border {
64 let b = &border.widths;
65 let brush = border.brush.clone();
66 if b.top > 0.0 {
67 canvas.fill_rect(x, y, w, b.top, brush.clone(), z + 1);
68 }
69 if b.right > 0.0 {
70 canvas.fill_rect(x + w - b.right, y, b.right, h, brush.clone(), z + 1);
71 }
72 if b.bottom > 0.0 {
73 canvas.fill_rect(x, y + h - b.bottom, w, b.bottom, brush.clone(), z + 1);
74 }
75 if b.left > 0.0 {
76 canvas.fill_rect(x, y, b.left, h, brush, z + 1);
77 }
78 }
79}
80
81pub fn draw_rounded_rectangle(
83 canvas: &mut Canvas,
84 rrect: RoundedRect,
85 fill: Option<Brush>,
86 stroke_width: Option<f32>,
87 stroke_brush: Option<Brush>,
88 z: i32,
89) {
90 if let Some(f) = fill {
91 canvas.rounded_rect(rrect, f, z);
92 }
93 if let (Some(w), Some(b)) = (stroke_width, stroke_brush) {
94 canvas.stroke_rounded_rect(rrect, w, b, z + 1);
95 }
96}
97
98pub fn draw_snapped_rounded_rectangle(
104 canvas: &mut Canvas,
105 mut rrect: RoundedRect,
106 fill: Option<Brush>,
107 stroke_width: Option<f32>,
108 stroke_brush: Option<Brush>,
109 z: i32,
110) {
111 let (x, y, w, h) = snapped_rect(
112 canvas,
113 rrect.rect.x,
114 rrect.rect.y,
115 rrect.rect.w,
116 rrect.rect.h,
117 );
118 rrect.rect.x = x;
119 rrect.rect.y = y;
120 rrect.rect.w = w;
121 rrect.rect.h = h;
122 draw_rounded_rectangle(canvas, rrect, fill, stroke_width, stroke_brush, z);
123}
124
125pub fn draw_circle(
127 canvas: &mut Canvas,
128 center: [f32; 2],
129 radius: f32,
130 fill: Option<Brush>,
131 stroke_width: Option<f32>,
132 stroke_brush: Option<Brush>,
133 z: i32,
134) {
135 if let Some(f) = fill {
136 canvas.circle(center, radius, f, z);
137 }
138 if let (Some(w), Some(sb)) = (stroke_width, stroke_brush) {
139 if let Brush::Solid(col) = sb {
141 let segs = 48u32;
142 let mut path = Path {
143 cmds: Vec::new(),
144 fill_rule: FillRule::NonZero,
145 };
146 let mut first = true;
147 for i in 0..=segs {
148 let t = (i as f32) / (segs as f32);
149 let ang = std::f32::consts::TAU * t;
150 let x = center[0] + radius * ang.cos();
151 let y = center[1] + radius * ang.sin();
152 if first {
153 path.cmds.push(PathCmd::MoveTo([x, y]));
154 first = false;
155 } else {
156 path.cmds.push(PathCmd::LineTo([x, y]));
157 }
158 }
159 path.cmds.push(PathCmd::Close);
160 canvas.stroke_path(path, w, col, z + 1);
161 }
162 }
163}
164
165pub fn draw_ellipse(
167 canvas: &mut Canvas,
168 center: [f32; 2],
169 radii: [f32; 2],
170 fill: Option<Brush>,
171 stroke_width: Option<f32>,
172 stroke_brush: Option<Brush>,
173 z: i32,
174) {
175 if let Some(f) = fill {
176 canvas.ellipse(center, radii, f, z);
177 }
178 if let (Some(w), Some(sb)) = (stroke_width, stroke_brush) {
179 if let Brush::Solid(col) = sb {
181 let segs = 64u32;
182 let mut path = Path {
183 cmds: Vec::new(),
184 fill_rule: FillRule::NonZero,
185 };
186 let mut first = true;
187 for i in 0..=segs {
188 let t = (i as f32) / (segs as f32);
189 let ang = std::f32::consts::TAU * t;
190 let x = center[0] + radii[0] * ang.cos();
191 let y = center[1] + radii[1] * ang.sin();
192 if first {
193 path.cmds.push(PathCmd::MoveTo([x, y]));
194 first = false;
195 } else {
196 path.cmds.push(PathCmd::LineTo([x, y]));
197 }
198 }
199 path.cmds.push(PathCmd::Close);
200 canvas.stroke_path(path, w, col, z + 1);
201 }
202 }
203}