hex_renderer/pattern/
draw_gradient.rs

1use 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 = colors[i];
30        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}