Euphoria
poisson.cc
Go to the documentation of this file.
1 #include "core/poisson.h"
2 
3 #include "base/numeric.h"
4 #include "base/random.h"
5 #include "assert/assert.h"
6 #include "base/cint.h"
7 
8 
9 namespace eu::core
10 {
11  // todo(Gustav): verify samples is inside Rect and considers the offset of the Rect
12 
13  bool
14  is_all_inside(const Rectf& a, const vec2f& p, float r)
15  {
16  return
17  a.left < p.x - r &&
18  a.right > p.x + r &&
19  a.top > p.y + r &&
20  a.bottom < p.y - r;
21  }
22 
23  PoissonWorker::PoissonWorker(const Rectf& aarea, Random* arandom, float ar, float bs, int ak)
24  : area(aarea)
25  , rand(arandom)
26  , r(ar)
27  , bounds_check(bs)
28  , k(ak)
29  , w(r / sqrt(2))
30  , grid(Table<int>::from_width_height(floor_to_int(area.get_width()/w), floor_to_int(area.get_height()/w), -1))
31  {
32  auto p = get_random_point_in_area();
33  if(bounds_check > 0)
34  {
35  for(int try_index=0; try_index<k; try_index+=1)
36  {
38  {
39  break;
40  }
41  else
42  {
44  }
45  }
46  }
47  const auto i = c_point_to_index(p);
48  active.emplace_back(0);
49  samples.emplace_back(p);
50  grid[{i.x, i.y}] = 0;
51  }
52 
53 
54  vec2f
56  { return get_random_point(rand, area); }
57 
58 
59  vec2i
61  { return vec2i{ floor_to_int(p.x/w), floor_to_int(p.y/w) }; }
62 
63 
64  bool
65  PoissonWorker::can_place_at(const vec2f& potential_sample, const vec2i& potential_sample_pos)
66  {
67  const int range = 3;
68  for(int dy=-range; dy<=range; dy+=1)
69  {
70  for(int dx=-range; dx<=range; dx+=1)
71  {
72  const auto neighbour_pos = potential_sample_pos + vec2i{dx, dy};
73  if(!grid.is_inside(neighbour_pos.x, neighbour_pos.y)) { continue; }
74  const auto neighbour_sample_index = grid[{neighbour_pos.x, neighbour_pos.y}];
75  if(neighbour_sample_index == -1) { continue; }
76  const auto d2 = vec2f::from_to(samples[neighbour_sample_index], potential_sample).get_length_squared();
77  if(d2 <= square(r))
78  {
79  return false;
80  }
81  }
82  }
83 
84  return true;
85  }
86 
87 
88  std::tuple<bool, vec2f>
89  PoissonWorker::try_place(int active_index)
90  {
91  const auto base_sample = samples[active[active_index]];
92 
93  for(int try_index = 0; try_index<k; try_index +=1)
94  {
95  const auto unit = create_random_unit(rand);
96  const auto random_range = get_random_in_range(rand, r, 2*r);
97  const auto sample = base_sample + unit * random_range;
98  const auto sample_pos = c_point_to_index(sample);
99 
100  if(!grid.is_inside(sample_pos.x, sample_pos.y)) { try_index -=1; continue;}
101 
102  if(bounds_check > 0)
103  {
104  if(!is_all_inside(area, sample, bounds_check)){ continue;}
105  }
106 
107  if(can_place_at(sample, sample_pos))
108  {
109  const auto point_index = c_sizet_to_int(samples.size());
110 
111  ASSERT((grid[{sample_pos.x, sample_pos.y}]) == -1);
112 
113  samples.emplace_back(sample);
114  grid[{sample_pos.x, sample_pos.y}] = point_index;
115  active.emplace_back(point_index);
116 
117  return {true, sample};
118  }
119  }
120 
121  return {false, zero2f};
122  }
123 
124 
125  bool
127  {
128  return active.empty();
129  }
130 
131 
132  std::optional<std::tuple<vec2f, vec2f>>
134  {
135  if(active.empty())
136  {
137  return std::nullopt;
138  }
139 
140  const auto active_index = get_random_in_range(rand, active.size());
141 
142  const auto [placed, sample] = try_place(c_sizet_to_int(active_index));
143  if(placed)
144  {
145  const vec2f from = samples[active[active_index]];
146  const vec2f to = sample; //*samples.rbegin();
147  return std::make_tuple(from, to);
148  }
149  else
150  {
151  active.erase(std::next(active.begin(), c_sizet_to_int(active_index)));
152  return std::nullopt;
153  }
154  }
155 
156 
157  std::vector<vec2f>
158  calc_poisson_samples(const Rectf& area, Random* random, float r, float bs, int k)
159  {
160  auto worker = PoissonWorker{area, random, r, bs, k};
161  while(!worker.is_done())
162  {
163  worker.update();
164  }
165  return worker.samples;
166  }
167 }
#define ASSERT(x)
Definition: assert.h:29
std::vector< vec2f > calc_poisson_samples(const Rectf &area, Random *random, float r, float bs, int k)
Definition: poisson.cc:158
bool is_all_inside(const Rectf &a, const vec2f &p, float r)
Definition: poisson.cc:14
float sqrt(float r)
Definition: numeric.cc:101
constexpr vec2f zero2f
Definition: vec2.h:68
int c_sizet_to_int(size_t t)
Definition: cint.cc:11
T get_random_in_range(Random *rand, const Range< T > &range)
Definition: random.h:50
float square(float r)
Definition: numeric.cc:94
unit2f create_random_unit(Random *random)
Definition: vec2.cc:435
int floor_to_int(float v)
Definition: numeric.cc:49
vec3f get_random_point(Random *rand, const Aabb &a)
Definition: aabb.cc:116
WEL512 Random Number Generator.
Definition: random.h:21
Definition: rect.h:27
vec2f get_random_point_in_area() const
Definition: poisson.cc:55
bool can_place_at(const vec2f &potential_sample, const vec2i &potential_sample_pos)
Definition: poisson.cc:65
std::vector< vec2f > samples
Definition: poisson.h:31
std::optional< std::tuple< vec2f, vec2f > > update()
Definition: poisson.cc:133
bool is_done() const
Definition: poisson.cc:126
Table< int > grid
Definition: poisson.h:29
std::tuple< bool, vec2f > try_place(int active_index)
Definition: poisson.cc:89
PoissonWorker(const Rectf &area, Random *random, float r, float bs, int k)
Definition: poisson.cc:23
std::vector< int > active
Definition: poisson.h:30
vec2i c_point_to_index(const vec2f &p) const
Definition: poisson.cc:60
bool is_inside(Idx x, Idx y) const
Definition: table.h:131
Definition: vec2.h:33
static vec2f from_to(const vec2f &from, const vec2f &to)
Definition: vec2.cc:83
float get_length_squared() const
Definition: vec2.cc:76
Definition: vec2.h:72