Krolyxon
Commit 2a1ff04

Branches

Tags

Refs →

bench51cratesbench51-guisrcicons.rs

200 lines · 6.4 KB · Rust

1//! Monoline toolbar icons drawn with the painter: 1.5 px strokes on a
2//! 16-unit grid, at most six strokes each, no fills.
3
4use std::f32::consts::TAU;
5
6use eframe::egui::{Color32, IconData, Painter, Pos2, Rect, Shape, Stroke, vec2};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum Icon {
10 New,
11 Open,
12 Save,
13 Build,
14 Debug,
15 Reset,
16 Run,
17 Pause,
18 StepInto,
19 StepOver,
20 StepOut,
21 RunToCursor,
22 Breakpoint,
23}
24
25pub fn paint(painter: &Painter, rect: Rect, icon: Icon, color: Color32) {
26 let s = rect.width().min(rect.height()) / 16.0;
27 let o = rect.center() - vec2(8.0, 8.0) * s;
28 let p = |x: f32, y: f32| o + vec2(x, y) * s;
29 let stroke = Stroke::new(1.5, color);
30 let line = |pts: &[(f32, f32)]| {
31 painter.add(Shape::line(
32 pts.iter().map(|&(x, y)| p(x, y)).collect(),
33 stroke,
34 ));
35 };
36 let closed = |pts: &[(f32, f32)]| {
37 painter.add(Shape::closed_line(
38 pts.iter().map(|&(x, y)| p(x, y)).collect(),
39 stroke,
40 ));
41 };
42 // Arc around (cx, cy) from angle a0 to a1 (radians, clockwise on screen).
43 let arc = |cx: f32, cy: f32, r: f32, a0: f32, a1: f32| -> Vec<Pos2> {
44 let n = 24;
45 (0..=n)
46 .map(|i| {
47 let a = a0 + (a1 - a0) * i as f32 / n as f32;
48 p(cx + r * a.cos(), cy + r * a.sin())
49 })
50 .collect()
51 };
52 let dot = |x: f32, y: f32| painter.circle_stroke(p(x, y), 1.5 * s, stroke);
53
54 match icon {
55 Icon::New => {
56 closed(&[
57 (3.0, 1.5),
58 (9.5, 1.5),
59 (13.0, 5.0),
60 (13.0, 14.5),
61 (3.0, 14.5),
62 ]);
63 line(&[(9.5, 1.5), (9.5, 5.0), (13.0, 5.0)]);
64 }
65 Icon::Open => {
66 closed(&[
67 (1.5, 3.0),
68 (6.0, 3.0),
69 (7.5, 4.5),
70 (14.5, 4.5),
71 (14.5, 13.0),
72 (1.5, 13.0),
73 ]);
74 line(&[(1.5, 7.0), (14.5, 7.0)]);
75 }
76 Icon::Save => {
77 line(&[(8.0, 1.5), (8.0, 10.0)]);
78 line(&[(4.5, 6.5), (8.0, 10.0), (11.5, 6.5)]);
79 line(&[(2.0, 10.0), (2.0, 14.0), (14.0, 14.0), (14.0, 10.0)]);
80 }
81 Icon::Build => {
82 // A DIP chip: body and four pins.
83 closed(&[(4.0, 3.5), (12.0, 3.5), (12.0, 12.5), (4.0, 12.5)]);
84 for y in [6.0, 10.0] {
85 line(&[(1.5, y), (4.0, y)]);
86 line(&[(12.0, y), (14.5, y)]);
87 }
88 }
89 Icon::Debug => {
90 // Power switch: starts and stops the debug session.
91 painter.add(Shape::line(
92 arc(8.0, 9.0, 5.5, -TAU * 0.17, TAU * 0.67),
93 stroke,
94 ));
95 line(&[(8.0, 1.5), (8.0, 8.0)]);
96 }
97 Icon::Reset => {
98 painter.add(Shape::line(
99 arc(8.0, 8.5, 5.5, -TAU * 0.2, TAU * 0.6),
100 stroke,
101 ));
102 let tip = (
103 8.0 + 5.5 * (TAU * 0.6f32).cos(),
104 8.5 + 5.5 * (TAU * 0.6f32).sin(),
105 );
106 line(&[(tip.0 - 3.5, tip.1 - 0.5), tip, (tip.0 + 0.5, tip.1 - 3.5)]);
107 }
108 Icon::Run => closed(&[(4.5, 2.5), (13.0, 8.0), (4.5, 13.5)]),
109 Icon::Pause => {
110 line(&[(5.5, 3.0), (5.5, 13.0)]);
111 line(&[(10.5, 3.0), (10.5, 13.0)]);
112 }
113 Icon::StepInto => {
114 line(&[(8.0, 1.5), (8.0, 9.5)]);
115 line(&[(4.5, 6.0), (8.0, 9.5), (11.5, 6.0)]);
116 dot(8.0, 13.5);
117 }
118 Icon::StepOver => {
119 painter.add(Shape::line(arc(8.0, 9.0, 5.5, TAU * 0.5, TAU), stroke));
120 line(&[(10.5, 8.0), (13.5, 9.0), (14.5, 6.0)]);
121 dot(8.0, 13.5);
122 }
123 Icon::StepOut => {
124 line(&[(8.0, 10.5), (8.0, 2.0)]);
125 line(&[(4.5, 5.5), (8.0, 2.0), (11.5, 5.5)]);
126 dot(8.0, 13.5);
127 }
128 Icon::RunToCursor => {
129 line(&[(1.5, 8.0), (11.0, 8.0)]);
130 line(&[(7.5, 4.5), (11.0, 8.0), (7.5, 11.5)]);
131 line(&[(14.5, 3.0), (14.5, 13.0)]);
132 }
133 Icon::Breakpoint => {
134 painter.circle_stroke(p(8.0, 8.0), 5.0 * s, stroke);
135 }
136 }
137}
138
139/// Window icon: a dot-matrix "51" in white on black with one red dot.
140pub fn app_icon() -> IconData {
141 const N: usize = 64;
142 // 5×7 glyphs.
143 const FIVE: [&str; 7] = [
144 "#####", "#....", "####.", "....#", "....#", "#...#", ".###.",
145 ];
146 const ONE: [&str; 7] = [
147 "..#..", ".##..", "..#..", "..#..", "..#..", "..#..", ".###.",
148 ];
149 let mut rgba = vec![0u8; N * N * 4];
150 // Rounded black tile.
151 let r = 12.0;
152 for y in 0..N {
153 for x in 0..N {
154 let (fx, fy) = (x as f32 + 0.5, y as f32 + 0.5);
155 let dx = (r - fx).max(fx - (N as f32 - r)).max(0.0);
156 let dy = (r - fy).max(fy - (N as f32 - r)).max(0.0);
157 let a = (r + 0.5 - (dx * dx + dy * dy).sqrt()).clamp(0.0, 1.0);
158 rgba[(y * N + x) * 4 + 3] = (a * 255.0) as u8;
159 }
160 }
161 let mut put_disc = |cx: f32, cy: f32, r: f32, c: [u8; 3]| {
162 for y in 0..N {
163 for x in 0..N {
164 let d = ((x as f32 + 0.5 - cx).powi(2) + (y as f32 + 0.5 - cy).powi(2)).sqrt();
165 let a = (r + 0.5 - d).clamp(0.0, 1.0);
166 if a > 0.0 {
167 let i = (y * N + x) * 4;
168 for k in 0..3 {
169 rgba[i + k] = (rgba[i + k] as f32 * (1.0 - a) + c[k] as f32 * a) as u8;
170 }
171 rgba[i + 3] = 255;
172 }
173 }
174 }
175 };
176 let pitch = 4.0;
177 let (x0, y0) = (11.0, 19.0);
178 for (gi, glyph) in [FIVE, ONE].iter().enumerate() {
179 for (row, bits) in glyph.iter().enumerate() {
180 for (col, b) in bits.bytes().enumerate() {
181 if b == b'#' {
182 let cx = x0 + (gi * 6 + col) as f32 * pitch + pitch / 2.0;
183 let cy = y0 + row as f32 * pitch + pitch / 2.0;
184 put_disc(cx, cy, 1.5, [255, 255, 255]);
185 }
186 }
187 }
188 }
189 put_disc(52.0, 12.0, 3.0, [0xD7, 0x19, 0x21]);
190 IconData {
191 rgba,
192 width: N as u32,
193 height: N as u32,
194 }
195}
196
197/// Square box for an icon of size `size` centred in `rect`.
198pub fn icon_rect(rect: Rect, size: f32) -> Rect {
199 Rect::from_center_size(rect.center(), vec2(size, size))
200}