1use cairo::Context;
2use n18hex::consts::*;
3use n18hex::{Colour, Hex};
4
5#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
7pub struct Tokens {
8 names: Vec<String>,
9 tokens: Vec<Token>,
10 count: usize,
11}
12
13impl From<Vec<(String, Token)>> for Tokens {
14 fn from(src: Vec<(String, Token)>) -> Self {
15 let mut names = vec![];
16 let mut tokens = vec![];
17 let count = src.len();
18 for (name, token) in src.into_iter() {
19 names.push(name);
20 tokens.push(token);
21 }
22 Self {
23 names,
24 tokens,
25 count,
26 }
27 }
28}
29
30impl From<Tokens> for Vec<(String, Token)> {
31 fn from(src: Tokens) -> Self {
32 src.names.into_iter().zip(src.tokens.into_iter()).collect()
33 }
34}
35
36impl Tokens {
37 pub fn new(src: Vec<(String, Token)>) -> Self {
38 src.into()
39 }
40
41 pub fn tokens(&self) -> &[Token] {
42 &self.tokens
43 }
44
45 pub fn names(&self) -> &[String] {
46 &self.names
47 }
48
49 pub fn count(&self) -> usize {
50 self.count
51 }
52
53 pub fn first_token(&self) -> Token {
54 self.tokens[0]
55 }
56
57 pub fn last_token(&self) -> Token {
58 self.tokens[self.count - 1]
59 }
60
61 pub fn prev_token(&self, token: &Token) -> Option<Token> {
62 self.tokens
63 .iter()
64 .enumerate()
65 .find(|(_ix, tok)| tok == &token)
66 .map(|(ix, _tok)| if ix > 0 { ix - 1 } else { self.count - 1 })
67 .map(|ix| self.tokens[ix])
68 }
69
70 pub fn next_token(&self, token: &Token) -> Option<Token> {
71 self.tokens
72 .iter()
73 .enumerate()
74 .find(|(_ix, tok)| tok == &token)
75 .map(|(ix, _tok)| if ix < self.count - 1 { ix + 1 } else { 0 })
76 .map(|ix| self.tokens[ix])
77 }
78
79 pub fn token(&self, name: &str) -> Option<&Token> {
80 self.names
81 .iter()
82 .enumerate()
83 .find(|(_ix, n)| n == &name)
84 .map(|(ix, _n)| &self.tokens[ix])
85 }
86
87 pub fn name(&self, token: &Token) -> Option<&str> {
88 self.tokens
89 .iter()
90 .enumerate()
91 .find(|(_ix, t)| t == &token)
92 .map(|(ix, _t)| self.names[ix].as_str())
93 }
94}
95
96#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
98pub struct Token {
99 pub style: TokenStyle,
100 pub x_pcnt: usize,
101 pub y_pcnt: usize,
102}
103
104#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
106pub enum TokenStyle {
107 SideArcs {
110 bg: Colour,
111 fg: Colour,
112 text: Colour,
113 },
114 TopArcs {
115 bg: Colour,
116 fg: Colour,
117 text: Colour,
118 },
119 TopSquares {
120 bg: Colour,
121 fg: Colour,
122 text: Colour,
123 },
124 TopLines {
125 bg: Colour,
126 fg: Colour,
127 text: Colour,
128 },
129 TopTriangles {
130 bg: Colour,
131 fg: Colour,
132 text: Colour,
133 },
134 TripleTriangles {
135 bg: Colour,
136 fg: Colour,
137 text: Colour,
138 },
139 TribandV {
140 sides: Colour,
141 middle: Colour,
142 text: Colour,
143 },
144 TribandH {
145 sides: Colour,
146 middle: Colour,
147 text: Colour,
148 },
149 TricolourV {
150 left: Colour,
151 middle: Colour,
152 right: Colour,
153 text: Colour,
154 },
155 TricolourH {
156 top: Colour,
157 middle: Colour,
158 bottom: Colour,
159 text: Colour,
160 },
161}
162
163impl TokenStyle {
164 fn draw_background(&self, hex: &Hex, ctx: &Context) {
165 use TokenStyle::*;
166
167 let radius = hex.theme.token_space_radius.absolute(hex);
168 let rmax = 1.1 * radius;
169 let dx = 0.45 * radius;
170
171 match self {
172 SideArcs { fg, bg, .. } => {
173 bg.apply_colour(ctx);
174 ctx.fill_preserve().unwrap();
175
176 ctx.clip_preserve();
177 ctx.new_path();
178 ctx.arc(-1.5 * radius, 0.0, 1.0 * radius, 0.0, 2.0 * PI);
179 ctx.arc(1.5 * radius, 0.0, 1.0 * radius, 0.0, 2.0 * PI);
180
181 fg.apply_colour(ctx);
182 ctx.fill().unwrap();
183 }
184 TopArcs { fg, bg, .. } => {
185 bg.apply_colour(ctx);
186 ctx.fill_preserve().unwrap();
187
188 ctx.clip_preserve();
189 ctx.new_path();
190 ctx.arc(0.0, -1.5 * radius, 1.0 * radius, 0.0, 2.0 * PI);
191 ctx.arc(0.0, 1.5 * radius, 1.0 * radius, 0.0, 2.0 * PI);
192
193 fg.apply_colour(ctx);
194 ctx.fill().unwrap();
195 }
196 TopSquares { fg, bg, .. } => {
197 bg.apply_colour(ctx);
198 ctx.fill_preserve().unwrap();
199 ctx.clip_preserve();
200 ctx.new_path();
201 ctx.move_to(-0.4 * radius, -dx);
202 ctx.line_to(0.4 * radius, -dx);
203 ctx.line_to(0.4 * radius, -rmax);
204 ctx.line_to(-0.4 * radius, -rmax);
205 fg.apply_colour(ctx);
206 ctx.fill().unwrap();
207 ctx.new_path();
208 ctx.move_to(-0.4 * radius, dx);
209 ctx.line_to(0.4 * radius, dx);
210 ctx.line_to(0.4 * radius, rmax);
211 ctx.line_to(-0.4 * radius, rmax);
212 fg.apply_colour(ctx);
213 ctx.fill().unwrap();
214 }
215 TopLines { fg, bg, .. } => {
216 bg.apply_colour(ctx);
217 ctx.fill_preserve().unwrap();
218 ctx.clip_preserve();
219 ctx.new_path();
220 ctx.move_to(-0.5 * radius, -dx);
221 ctx.line_to(-0.3 * radius, -dx);
222 ctx.line_to(-0.3 * radius, -rmax);
223 ctx.line_to(-0.5 * radius, -rmax);
224 ctx.move_to(-0.1 * radius, -dx);
225 ctx.line_to(0.1 * radius, -dx);
226 ctx.line_to(0.1 * radius, -rmax);
227 ctx.line_to(-0.1 * radius, -rmax);
228 ctx.move_to(0.5 * radius, -dx);
229 ctx.line_to(0.3 * radius, -dx);
230 ctx.line_to(0.3 * radius, -rmax);
231 ctx.line_to(0.5 * radius, -rmax);
232 fg.apply_colour(ctx);
233 ctx.fill().unwrap();
234 ctx.new_path();
235 ctx.move_to(-0.5 * radius, dx);
236 ctx.line_to(-0.3 * radius, dx);
237 ctx.line_to(-0.3 * radius, rmax);
238 ctx.line_to(-0.5 * radius, rmax);
239 ctx.move_to(-0.1 * radius, dx);
240 ctx.line_to(0.1 * radius, dx);
241 ctx.line_to(0.1 * radius, rmax);
242 ctx.line_to(-0.1 * radius, rmax);
243 ctx.move_to(0.5 * radius, dx);
244 ctx.line_to(0.3 * radius, dx);
245 ctx.line_to(0.3 * radius, rmax);
246 ctx.line_to(0.5 * radius, rmax);
247 fg.apply_colour(ctx);
248 ctx.fill().unwrap();
249 }
250 TopTriangles { fg, bg, .. } => {
251 bg.apply_colour(ctx);
252 ctx.fill_preserve().unwrap();
253 ctx.clip_preserve();
254 ctx.new_path();
255 ctx.move_to(-dx, -rmax);
256 ctx.line_to(0.0, -dx);
257 ctx.line_to(dx, -rmax);
258 fg.apply_colour(ctx);
259 ctx.fill().unwrap();
260 ctx.new_path();
261 ctx.move_to(-dx, rmax);
262 ctx.line_to(0.0, dx);
263 ctx.line_to(dx, rmax);
264 fg.apply_colour(ctx);
265 ctx.fill().unwrap();
266 }
267 TripleTriangles { fg, bg, .. } => {
268 bg.apply_colour(ctx);
269 ctx.fill_preserve().unwrap();
270 ctx.clip_preserve();
271 ctx.new_path();
272 ctx.move_to(-1.5 * radius, -rmax);
273 ctx.line_to(-0.3 * radius, -dx);
274 ctx.line_to(-0.3 * radius, -rmax);
275 ctx.line_to(0.0, -0.5 * radius);
276 ctx.line_to(0.3 * radius, -rmax);
277 ctx.line_to(0.3 * radius, -dx);
278 ctx.line_to(1.5 * radius, -rmax);
279 fg.apply_colour(ctx);
280 ctx.fill().unwrap();
281 ctx.new_path();
282 ctx.move_to(-1.5 * radius, rmax);
283 ctx.line_to(-0.3 * radius, dx);
284 ctx.line_to(-0.3 * radius, rmax);
285 ctx.line_to(0.0, 0.5 * radius);
286 ctx.line_to(0.3 * radius, rmax);
287 ctx.line_to(0.3 * radius, dx);
288 ctx.line_to(1.5 * radius, rmax);
289 fg.apply_colour(ctx);
290 ctx.fill().unwrap();
291 }
292 TribandV { sides, middle, .. } => {
293 sides.apply_colour(ctx);
294 ctx.fill_preserve().unwrap();
295 ctx.clip_preserve();
296 ctx.new_path();
297 ctx.move_to(-dx, rmax);
298 ctx.line_to(-dx, -rmax);
299 ctx.line_to(dx, -rmax);
300 ctx.line_to(dx, rmax);
301 ctx.line_to(-dx, rmax);
302 middle.apply_colour(ctx);
303 ctx.fill().unwrap();
304 }
305 TribandH { sides, middle, .. } => {
306 sides.apply_colour(ctx);
307 ctx.fill_preserve().unwrap();
308 ctx.clip_preserve();
309 ctx.new_path();
310 ctx.move_to(rmax, -dx);
311 ctx.line_to(-rmax, -dx);
312 ctx.line_to(-rmax, dx);
313 ctx.line_to(rmax, dx);
314 ctx.line_to(rmax, -dx);
315 middle.apply_colour(ctx);
316 ctx.fill().unwrap();
317 }
318 TricolourV {
319 left,
320 middle,
321 right,
322 ..
323 } => {
324 middle.apply_colour(ctx);
326 ctx.fill_preserve().unwrap();
327 ctx.clip_preserve();
328 ctx.new_path();
330 ctx.move_to(-rmax, -rmax);
331 ctx.line_to(-rmax, rmax);
332 ctx.line_to(-dx, rmax);
333 ctx.line_to(-dx, -rmax);
334 left.apply_colour(ctx);
335 ctx.fill().unwrap();
336 ctx.new_path();
338 ctx.move_to(rmax, -rmax);
339 ctx.line_to(rmax, rmax);
340 ctx.line_to(dx, rmax);
341 ctx.line_to(dx, -rmax);
342 right.apply_colour(ctx);
343 ctx.fill().unwrap();
344 }
345 TricolourH {
346 top,
347 middle,
348 bottom,
349 ..
350 } => {
351 middle.apply_colour(ctx);
353 ctx.fill_preserve().unwrap();
354 ctx.clip_preserve();
355 ctx.new_path();
357 ctx.move_to(-rmax, -dx);
358 ctx.line_to(-rmax, -rmax);
359 ctx.line_to(rmax, -rmax);
360 ctx.line_to(rmax, -dx);
361 top.apply_colour(ctx);
362 ctx.fill().unwrap();
363 ctx.new_path();
365 ctx.move_to(-rmax, dx);
366 ctx.line_to(-rmax, rmax);
367 ctx.line_to(rmax, rmax);
368 ctx.line_to(rmax, dx);
369 bottom.apply_colour(ctx);
370 ctx.fill().unwrap();
371 }
372 }
373 }
374
375 pub fn text_colour(&self) -> &Colour {
376 use TokenStyle::*;
377
378 match self {
379 SideArcs { text, .. } => text,
380 TopArcs { text, .. } => text,
381 TopSquares { text, .. } => text,
382 TopLines { text, .. } => text,
383 TopTriangles { text, .. } => text,
384 TripleTriangles { text, .. } => text,
385 TribandV { text, .. } => text,
386 TribandH { text, .. } => text,
387 TricolourV { text, .. } => text,
388 TricolourH { text, .. } => text,
389 }
390 }
391}
392
393impl Token {
394 pub fn new(style: TokenStyle) -> Self {
395 Self {
396 style,
397 x_pcnt: 50,
398 y_pcnt: 50,
399 }
400 }
401
402 pub fn shift_text(mut self, x_pcnt: usize, y_pcnt: usize) -> Self {
403 self.x_pcnt = x_pcnt;
404 self.y_pcnt = y_pcnt;
405 self
406 }
407
408 fn draw_text(&self, hex: &Hex, ctx: &Context, text: &str) {
409 let mut labeller = hex.theme.token_label.labeller(ctx, hex);
411
412 labeller.halign(n18hex::theme::AlignH::Centre);
414 labeller.valign(n18hex::theme::AlignV::Middle);
415 labeller.colour(*self.style.text_colour());
416
417 let (x, y) = ctx.current_point().unwrap();
420 let radius = hex.theme.token_space_radius.absolute(hex);
421 let dx = radius * ((self.x_pcnt as f64 - 50.0) / 50.0);
422 let dy = radius * ((self.y_pcnt as f64 - 50.0) / 50.0);
423 let text_centre = n18hex::Coord::from((x + dx, y + dy));
424
425 labeller.draw(text, text_centre);
426 }
427
428 pub fn draw(&self, hex: &Hex, ctx: &Context, text: &str, rotn: f64) {
432 let (x0, y0, x1, y1) = ctx.fill_extents().unwrap();
434 let x = 0.5 * (x0 + x1);
435 let y = 0.5 * (y0 + y1);
436
437 let m = ctx.matrix();
438 ctx.save().unwrap();
439
440 ctx.translate(x, y);
442 ctx.rotate(-rotn);
443
444 let stroke_path = ctx.copy_path().unwrap();
445 self.style.draw_background(hex, ctx);
446 self.draw_text(hex, ctx, text);
447
448 ctx.new_path();
450 ctx.append_path(&stroke_path);
451 hex.theme.token_space_inner.apply_line_and_stroke(ctx, hex);
452 ctx.stroke_preserve().unwrap();
453
454 ctx.restore().unwrap();
455 ctx.set_matrix(m);
456 }
457}