hex_renderer/pattern/
draw_gradient.rs1use std::collections::HashMap;
2
3use tiny_skia::{
4 GradientStop, LinearGradient, Paint, Pixmap, SpreadMode, Stroke, Transform,
5};
6
7use crate::pattern_utils::{Coord, HexCoord, LineDrawer};
8
9use crate::options::Color;
10
11use super::Pattern;
12
13#[allow(clippy::too_many_arguments)]
14pub fn draw_gradient_lines(
15 pattern: &Pattern,
16 pixmap: &mut Pixmap,
17 stroke: &Stroke,
18 origin: HexCoord,
19 scale: f32,
20 colors: &[Color],
21 segs_per_color: usize,
22 bent_corners: bool,
23) -> Color {
24 let segments = pattern.path.len() as f32 - 1.0;
25
26 let mut grad_colors = Vec::new();
27
28 for col in colors.iter().take(pattern.path.len() / segs_per_color + 2) {
29 let col = tiny_skia::Color::from(*col);
31 grad_colors.push([col.red(), col.green(), col.blue(), col.alpha()]);
32 }
33
34 let mut grad_diffs = Vec::new();
35
36 for i in 1..grad_colors.len() {
37 let mut col = [0.0; 4];
38 for (j, col) in col.iter_mut().enumerate() {
39 *col = grad_colors[i][j] - grad_colors[i - 1][j];
40 }
41 grad_diffs.push(col);
42 }
43
44 let mut cur_color = grad_colors[0];
45
46 let grad_segments = grad_colors.len() - 1;
47
48 let mut loc_prev = origin + HexCoord::from(pattern.path[0]) * scale;
49
50 let mut visit_count: HashMap<Coord, usize> = HashMap::new();
51
52 if bent_corners {
53 for path in &pattern.path {
54 if let Some(count) = visit_count.get_mut(path) {
55 *count += 1;
56 } else {
57 visit_count.insert(*path, 1);
58 }
59 }
60 }
61 let paint = Paint::default();
62 let mut line_drawer = LineDrawer::new(origin, stroke.clone(), paint);
63
64 let mut prev_shade_color =
65 tiny_skia::Color::from_rgba(cur_color[0], cur_color[1], cur_color[2], cur_color[3]).unwrap();
66
67 for i in 1..pattern.path.len() {
68 let mut loc_next = origin + HexCoord::from(pattern.path[i]) * scale;
69
70 let progress = (i - 1) as f32 / segments;
71 let grad_seg = (progress * grad_segments as f32) as usize;
72
73 let seg_progress =
74 (progress - (grad_seg as f32 / grad_segments as f32)) * grad_segments as f32;
75
76 for (j, cur_color) in cur_color.iter_mut().enumerate() {
77 *cur_color =
78 (grad_colors[grad_seg][j] + grad_diffs[grad_seg][j] * seg_progress).clamp(0.0, 1.0);
79 }
80
81 let cur_col =
82 tiny_skia::Color::from_rgba(cur_color[0], cur_color[1], cur_color[2], cur_color[3]).unwrap();
83
84 line_drawer.set_shader(
85 LinearGradient::new(
86 tiny_skia::Point::from_xy(loc_prev.0, loc_prev.1),
87 tiny_skia::Point::from_xy(loc_next.0, loc_next.1),
88 vec![
89 GradientStop::new(0.0, prev_shade_color),
90 GradientStop::new(1.0, cur_col),
91 ],
92 SpreadMode::Pad,
93 Transform::identity(),
94 )
95 .unwrap(),
96 );
97
98 if bent_corners
99 && visit_count.get(&pattern.path[i]).unwrap() > &1
100 && pattern.path.len() - 1 != i
101 {
102 let bend_amount = 0.2;
103
104 let stop_point = loc_next - (loc_next - loc_prev) * bend_amount;
105 line_drawer.line_to(stop_point);
106
107 loc_next = loc_next
108 + (origin + HexCoord::from(pattern.path[i + 1]) * scale - loc_next) * bend_amount;
109 }
110 line_drawer.line_to(loc_next);
111
112 loc_prev = loc_next;
113 prev_shade_color = cur_col;
114 }
115
116 line_drawer.draw_all(pixmap);
117
118 colors[grad_colors.len() - 1]
119}