experimental SVG-based video rendering engine made for music videos. React to MIDI or arbitrary signals from your DAW through "probe" VSTs
1use crate::{Containable, Point, Region};
2use rand::{Rng, distr::uniform::SampleRange, seq::IteratorRandom};
3
4impl Region {
5 pub fn random_end(&self, rng: &mut impl Rng, start: Point) -> Point {
6 // End anchors are always a square diagonal from the start anchor (for now)
7 // that means taking steps of the form n * (one of (1, 1), (1, -1), (-1, 1), (-1, -1))
8 // Except that the end anchor needs to stay in the bounds of the shape.
9
10 // Determine all possible end anchors that are in a square diagonal from the start anchor
11 let mut possible_end_anchors = vec![];
12
13 // shapes can end on the next cell, since that's where they end
14 let actual_region = self.enlarged(1, 1);
15
16 for x in actual_region.mirrored_width_range() {
17 for y in actual_region.mirrored_height_range() {
18 let end_anchor = start.translated(x, y);
19
20 if end_anchor == start {
21 continue;
22 }
23
24 // Check that the end anchor is in a square diagonal from the start anchor and that the end anchor is in bounds
25 if x.abs() == y.abs() && actual_region.contains(&end_anchor) {
26 possible_end_anchors.push(end_anchor);
27 }
28 }
29 }
30
31 // Pick a random end anchor from the possible end anchors
32 possible_end_anchors[rng.random_range(0..possible_end_anchors.len())]
33 }
34
35 pub fn random(rng: &mut impl Rng, within: &Region) -> Self {
36 let start = Point::random(rng, within);
37 let end = within.random_end(rng, start);
38 Region::from(if start.x() > end.x() {
39 (end, start)
40 } else {
41 (start, end)
42 })
43 }
44
45 pub fn random_point(&self, rng: &mut impl Rng) -> Point {
46 Point::random(rng, self)
47 }
48
49 pub fn random_point_except(
50 &self,
51 rng: &mut impl Rng,
52 except: &Region,
53 ) -> Point {
54 self.iter()
55 .filter(|p| !except.contains(p))
56 .choose(rng)
57 .unwrap()
58 }
59}
60
61impl SampleRange<Point> for Region {
62 fn is_empty(&self) -> bool {
63 self.is_empty()
64 }
65
66 fn sample_single<R: rand::RngCore + ?Sized>(
67 self,
68 rng: &mut R,
69 ) -> Result<Point, rand::distr::uniform::Error> {
70 if self.is_empty() {
71 return Err(rand::distr::uniform::Error::EmptyRange);
72 }
73
74 Ok(Point::Corner(
75 rng.random_range(self.x_range()),
76 rng.random_range(self.y_range()),
77 ))
78 }
79}