1use wassily_core::{canvas::*, shape::*, points::pt};
48use wassily_color::*;
49use wassily_noise::*;
50use crate::stipple::uniform;
51use noise::core::worley::distance_functions::euclidean_squared;
52use noise::core::worley::ReturnType;
53use noise::*;
54use tiny_skia::*;
55pub struct PatternPaint<'a> {
56 pub texture_canvas: &'a Canvas,
57 pub pattern_canvas: Canvas,
58 pub bbox: Rect,
59}
60
61impl<'a> PatternPaint<'a> {
62 pub fn new(texture_canvas: &'a Canvas, bbox: Rect, width: u32, height: u32) -> Self {
63 let pattern_canvas = Canvas::new(width, height);
64 Self {
65 texture_canvas,
66 pattern_canvas,
67 bbox,
68 }
69 }
70
71 pub fn paint(&'a mut self) -> Paint<'a> {
72 let x = self.bbox.x();
73 let y = self.bbox.y();
74 self.pattern_canvas.pixmap.draw_pixmap(
75 x as i32,
76 y as i32,
77 self.texture_canvas.pixmap.as_ref(),
78 &PixmapPaint::default(),
79 Transform::identity(),
80 None,
81 );
82 let pattern = Pattern::new(
83 self.pattern_canvas.pixmap.as_ref(),
84 SpreadMode::Pad,
85 FilterQuality::Bicubic,
86 1.0,
87 Transform::identity(),
88 );
89 paint_shader(pattern)
90 }
91}
92
93pub fn stipple_texture(width: u32, height: u32, color: Color, spacing: f32) -> Canvas {
94 let mut canvas = Canvas::new(width, height);
95 let n = canvas.w_f32() * canvas.h_f32() / spacing;
96 let dots = uniform(width as f32, height as f32, n as u32, 0);
97 for d in dots {
98 canvas.dot(d.x, d.y, color);
99 }
100 canvas
101}
102
103pub fn horizontal_stripe(
104 width: u32,
105 height: u32,
106 color1: Color,
107 color2: Color,
108 spacing: f32,
109) -> Canvas {
110 let mut canvas = Canvas::new(width, height);
111 let mut l = 0.0;
112 canvas.pixmap.fill(color1);
113 while l < height as f32 {
114 Shape::new()
115 .line(pt(0.0, l), pt(width, l))
116 .stroke_color(color2)
117 .stroke_weight(4.0)
118 .draw(&mut canvas);
119 l += spacing;
120 }
121 canvas
122}
123
124pub fn ridge(width: u32, height: u32, color1: Color, color2: Color, scale: f32) -> Canvas {
125 let mut canvas = Canvas::new(width, height);
126 let nf = RidgedMulti::<Perlin>::default();
127 let opts = NoiseOpts::with_wh(width, height).scales(scale);
128 for i in 0..width {
129 for j in 0..height {
130 let a = noise2d_01(&nf, &opts, i as f32, j as f32);
131 let c = color1.lerp(&color2, a);
132 canvas.dot(i as f32, j as f32, c);
133 }
134 }
135 canvas
136}
137
138pub fn foam(
139 width: u32,
140 height: u32,
141 color1: Color,
142 color2: Color,
143 color3: Color,
144 scale: f32,
145 seed: u32,
146) -> Canvas {
147 let mut canvas = Canvas::new(width, height);
148 let nf = Worley::default()
149 .set_distance_function(euclidean_squared)
150 .set_return_type(ReturnType::Distance)
151 .set_seed(seed);
152 let opts = NoiseOpts::with_wh(width, height).scales(scale);
153 for i in 0..width {
154 for j in 0..height {
155 let a = noise2d_01(&nf, &opts, i as f32, j as f32);
156 let mut c = color1.lerp(&color2, a);
157 if a > 0.48 && a < 0.52 {
158 c = color3
159 }
160 canvas.dot(i as f32, j as f32, c);
161 }
162 }
163 canvas
164}
165
166pub fn marble(
167 width: u32,
168 height: u32,
169 color1: Color,
170 color2: Color,
171 scale: f32,
172 seed: u32,
173) -> Canvas {
174 let mut canvas = Canvas::new(width, height);
175 let nf = Fbm::<Perlin>::default().set_seed(seed);
176 let opts = NoiseOpts::with_wh(width, height).scales(scale);
177 for i in 0..width {
178 for j in 0..height {
179 let a = noise2d(&nf, &opts, i as f32, j as f32);
180 let b =
181 0.5 * (((j as f32 + a * 1000.0) * std::f32::consts::PI * 2.0 / 200.0).sin() + 1.0);
182 let c = color1.lerp(&color2, b);
183 canvas.dot(i as f32, j as f32, c);
184 }
185 }
186 canvas
187}
188
189pub fn wood(width: u32, height: u32, color1: Color, color2: Color, scale: f32) -> Canvas {
191 let mut canvas = Canvas::new(width, height);
192 let base_wood = Cylinders::new().set_frequency(16.0);
193
194 let wood_grain_noise = BasicMulti::<Perlin>::new(0)
196 .set_frequency(48.0)
197 .set_persistence(0.5)
198 .set_lacunarity(2.20703125)
199 .set_octaves(3);
200
201 let scaled_base_wood_grain = ScalePoint::new(wood_grain_noise).set_z_scale(0.25);
204
205 let wood_grain = ScaleBias::new(scaled_base_wood_grain)
207 .set_scale(0.25)
208 .set_bias(0.125);
209
210 let combined_wood = Add::new(base_wood, wood_grain);
212
213 let perturbed_wood = Turbulence::<_, Perlin>::new(combined_wood)
215 .set_seed(1)
216 .set_frequency(4.0)
217 .set_power(1.0 / 256.0)
218 .set_roughness(4);
219
220 let nf = Turbulence::<_, Perlin>::new(perturbed_wood)
221 .set_seed(2)
222 .set_frequency(2.0)
223 .set_power(1.0 / 64.0)
224 .set_roughness(4);
225 let opts = NoiseOpts::with_wh(width, height).scales(scale);
226 for i in 0..width {
227 for j in 0..height {
228 let b = noise2d_01(
229 &nf,
230 &opts,
231 i as f32 - width as f32 / 2.0,
232 j as f32 - height as f32 / 2.0,
233 );
234 let c = color1.lerp(&color2, b);
235 canvas.dot(i as f32, j as f32, c);
236 }
237 }
238 canvas
239}
240
241pub fn granite(
243 width: u32,
244 height: u32,
245 color1: Color,
246 color2: Color,
247 scale: f32,
248 seed: u32,
249) -> Canvas {
250 let mut canvas = Canvas::new(width, height);
251 let primary_granite = Billow::<Perlin>::new(0)
254 .set_frequency(8.0)
255 .set_persistence(0.625)
256 .set_lacunarity(2.18359375)
257 .set_octaves(6)
258 .set_seed(seed);
259
260 let base_grains = Worley::new(1)
262 .set_frequency(16.0)
263 .set_return_type(ReturnType::Distance);
264
265 let scaled_grains = ScaleBias::new(base_grains).set_scale(-0.5).set_bias(0.0);
269
270 let combined_granite = Add::new(primary_granite, scaled_grains);
272
273 let nf = Turbulence::<_, Perlin>::new(combined_granite)
275 .set_seed(2)
276 .set_frequency(4.0)
277 .set_power(1.0 / 8.0)
278 .set_roughness(6);
279 let opts = NoiseOpts::with_wh(width, height).scales(scale);
280 for i in 0..width {
281 for j in 0..height {
282 let b = noise2d_01(&nf, &opts, i as f32, j as f32);
283 let c = color1.lerp(&color2, b);
284 canvas.dot(i as f32, j as f32, c);
285 }
286 }
287 canvas
288}