old school music tracker
1use std::ops::{Add, RangeInclusive};
2
3/// font size in pixel. font is a square
4pub const FONT_SIZE: usize = 8;
5/// window size in characters
6pub const WINDOW_SIZE_CHARS: (usize, usize) = (80, 50);
7/// window size in pixel
8pub const WINDOW_SIZE: (usize, usize) = (
9 FONT_SIZE * WINDOW_SIZE_CHARS.0,
10 FONT_SIZE * WINDOW_SIZE_CHARS.1,
11);
12
13/// CharRect as well as PixelRect uses all values inclusive, meaning the borders are included
14#[derive(Debug, Clone, Copy)]
15pub struct CharRect {
16 top: usize,
17 bot: usize,
18 left: usize,
19 right: usize,
20}
21
22impl CharRect {
23 /// row 11 is reserved for the page title. The page shouldn't draw on it
24 pub const PAGE_AREA: Self = Self::new(12, WINDOW_SIZE_CHARS.1 - 1, 0, WINDOW_SIZE_CHARS.0 - 1);
25 pub const HEADER_AREA: Self = Self::new(0, 10, 0, WINDOW_SIZE_CHARS.0 - 1);
26
27 pub const fn new(top: usize, bot: usize, left: usize, right: usize) -> Self {
28 assert!(top <= bot, "top needs to be smaller than bot");
29 assert!(left <= right, "left needs to be smaller than right");
30 assert!(bot < WINDOW_SIZE_CHARS.1, "lower than window bounds");
31 assert!(right < WINDOW_SIZE_CHARS.0, "right out of window bounds");
32
33 Self {
34 top,
35 bot,
36 left,
37 right,
38 }
39 }
40
41 pub const fn top(self) -> usize {
42 self.top
43 }
44 pub const fn bot(self) -> usize {
45 self.bot
46 }
47 pub const fn right(self) -> usize {
48 self.right
49 }
50 pub const fn left(self) -> usize {
51 self.left
52 }
53
54 pub const fn top_left(self) -> CharPosition {
55 CharPosition {
56 x: self.left,
57 y: self.top,
58 }
59 }
60
61 pub const fn width(self) -> usize {
62 self.right - self.left
63 }
64
65 pub const fn height(self) -> usize {
66 self.bot - self.top
67 }
68}
69
70/// uncheck conversion, because CharPosition is a safe type
71impl From<CharPosition> for CharRect {
72 fn from(value: CharPosition) -> Self {
73 Self::new(value.y, value.y, value.x, value.x)
74 // Self {
75 // top: value.y,
76 // bot: value.y,
77 // left: value.x,
78 // right: value.x,
79 // }
80 }
81}
82
83/// PixelRect as well as CharRect uses all values inclusive, meaning the borders are included
84#[derive(Debug, Clone, Copy)]
85pub struct PixelRect {
86 top: usize,
87 bot: usize,
88 right: usize,
89 left: usize,
90}
91
92impl PixelRect {
93 pub const fn new(top: usize, bot: usize, right: usize, left: usize) -> Self {
94 assert!(top <= bot, "top needs to be smaller than bot");
95 assert!(left <= right, "left needs to be smaller than right");
96 assert!(bot < WINDOW_SIZE.1, "lower than window bounds");
97 assert!(right < WINDOW_SIZE.0, "right out of window bounds");
98
99 Self {
100 top,
101 bot,
102 right,
103 left,
104 }
105 }
106
107 pub const fn vertical_range(&self) -> RangeInclusive<usize> {
108 RangeInclusive::new(self.top, self.bot)
109 }
110
111 pub const fn horizontal_range(&self) -> RangeInclusive<usize> {
112 RangeInclusive::new(self.left, self.right)
113 }
114
115 pub const fn top(&self) -> usize {
116 self.top
117 }
118
119 pub const fn bot(&self) -> usize {
120 self.bot
121 }
122
123 pub const fn right(&self) -> usize {
124 self.right
125 }
126
127 pub const fn left(&self) -> usize {
128 self.left
129 }
130}
131
132/// unchecked conversion because CharRect is a safe type
133impl From<CharRect> for PixelRect {
134 fn from(value: CharRect) -> Self {
135 Self::new(
136 value.top * FONT_SIZE,
137 (value.bot * FONT_SIZE) + FONT_SIZE - 1,
138 (value.right * FONT_SIZE) + FONT_SIZE - 1,
139 value.left * FONT_SIZE,
140 )
141 // Self {
142 // top: value.top * FONT_SIZE,
143 // bot: (value.bot * FONT_SIZE) + FONT_SIZE - 1,
144 // right: (value.right * FONT_SIZE) + FONT_SIZE - 1,
145 // left: value.left * FONT_SIZE,
146 // }
147 }
148}
149
150/// unchecked conversion, because CharPosition is a safe type
151impl From<CharPosition> for PixelRect {
152 fn from(value: CharPosition) -> Self {
153 Self::from(CharRect::from(value))
154 }
155}
156
157#[derive(Debug, Clone, Copy)]
158pub struct CharPosition {
159 x: usize,
160 y: usize,
161}
162
163impl CharPosition {
164 #[track_caller]
165 pub const fn new(x: usize, y: usize) -> Self {
166 assert!(y < WINDOW_SIZE_CHARS.1);
167 assert!(x < WINDOW_SIZE_CHARS.0);
168
169 Self { x, y }
170 }
171
172 pub const fn x(&self) -> usize {
173 self.x
174 }
175
176 pub const fn y(&self) -> usize {
177 self.y
178 }
179}
180
181impl Add for CharPosition {
182 type Output = Self;
183
184 #[track_caller]
185 fn add(self, rhs: Self) -> Self::Output {
186 Self::new(self.x + rhs.x, self.y + rhs.y)
187 }
188}
189
190impl Add<(usize, usize)> for CharPosition {
191 type Output = Self;
192
193 #[track_caller]
194 fn add(self, rhs: (usize, usize)) -> Self::Output {
195 Self::new(self.x + rhs.0, self.y + rhs.1)
196 }
197}
198
199// impl Mul<usize> for CharPosition {
200// type Output = Self;
201
202// fn mul(self, rhs: usize) -> Self::Output {
203// Self::new(self.x * rhs, self.y * rhs)
204// }
205// }