/
randompoints.jl
157 lines (139 loc) · 4.45 KB
/
randompoints.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
function randomordinate(low, high)
if low > high
low, high = high, low
end
return low + rand() * abs(high - low)
end
"""
randompoint(lowpt, highpt)
Return a random point somewhere inside the rectangle defined by the two points.
"""
function randompoint(lowpt::Point, highpt::Point)
Point(randomordinate(lowpt.x, highpt.x), randomordinate(lowpt.y, highpt.y))
end
"""
randompoint(lowx, lowy, highx, highy)
Return a random point somewhere inside a rectangle defined by the four values.
"""
function randompoint(lowx, lowy, highx, highy)
Point(randomordinate(lowx, highx), randomordinate(lowy, highy))
end
"""
randompointarray(lowpt, highpt, n)
Return an array of `n` random points somewhere inside the rectangle defined by two points.
"""
function randompointarray(lowpt::Point, highpt::Point, n)
array = Point[]
for i in 1:n
push!(array, randompoint(lowpt, highpt))
end
array
end
"""
randompointarray(lowx, lowy, highx, highy, n)
Return an array of `n` random points somewhere inside the rectangle defined by the four
coordinates.
"""
function randompointarray(lowx, lowy, highx, highy, n)
array = Point[]
for i in 1:n
push!(array, randompoint(lowx, lowy, highx, highy))
end
array
end
# internal function used by Poisson disk sampling below
"""
_empty_neighbourhood(sample, w, h, cellsize, d, points, grid)
Uses entries in `grid` to check whether the `sample`
point is more than `d` units away from any other point
in `points`.
The region we're analyzing lies between the origin and
`Point(w, h)``.
"""
function _empty_neighbourhood(sample::Point, w, h, cellsize, d, points::Array{Point, 1}, grid)
if sample.x >= 0 &&
sample.x < w &&
sample.y >= 0 &&
sample.y < h
cellx = 1 + convert(Int, ceil(sample.x/cellsize))
celly = 1 + convert(Int, ceil(sample.y/cellsize))
_w, _h = size(grid)
sstartx = max(1, cellx - 2)
sfinishx = min(cellx + 2, _w)
sstarty = max(1, celly - 2)
sfinishy = min(celly + 2, _h)
for x in sstartx:sfinishx
for y = sstarty:sfinishy
ptindex = grid[x, y]
if ptindex > 0
dist = distance(sample, points[ptindex])
if dist < d
return false
end
end
end
end
return true
end
return false
end
"""
randompointarray(w, h, d; attempts=20)
Return an array of randomly positioned points inside the
rectangle defined by the current origin (0/0) and the
`width` and `height`. `d` determines the minimum
distance between each point. Increase `attempts` if you want
the function to try harder to fill empty spaces; decrease it
if it's taking too long to look for samples that work.
This uses Bridson's Poisson Disk Sampling algorithm:
https://www.cs.ubc.ca/~rbridson/docs/bridson-siggraph07-poissondisk.pdf
## Example
```
for pt in randompointarray(BoundingBox(), 20)
randomhue()
circle(pt, 10, :fill)
end
```
"""
function randompointarray(w, h, d;
attempts=20)
cellsize = d/sqrt(2)
grid = zeros(Int,
convert(Int, ceil(w/cellsize)),
convert(Int, ceil(h/cellsize)))
points = Point[]
activepoints = Point[]
# start with point in middle
push!(activepoints, Point(w/2, h/2))
while !isempty(activepoints)
random_index = rand(1:length(activepoints))
sample_center = activepoints[random_index]
sample_added = false
for i in 1:attempts
angle = rand() * 2π
dir = (sin(angle), cos(angle))
sample = sample_center + dir .* (d + rand() * d)
if _empty_neighbourhood(sample, w, h, cellsize, d, points, grid)
push!(points, sample)
push!(activepoints, sample)
grid[convert(Int, ceil(sample.x/cellsize)), convert(Int, ceil(sample.y/cellsize))] = length(points)
sample_added = true
break
end
end
if !sample_added
deleteat!(activepoints, random_index)
end
end
return points
end
"""
randompointarray(bbox::BoundingBox, d; attempts=20)
Return an array of randomly positioned points inside the
bounding box `d` units apart.
"""
function randompointarray(bbox::BoundingBox, d; kwargs...)
bw = boxwidth(bbox)
bh = boxheight(bbox)
return randompointarray(bw, bh, d; kwargs...) .- (Point(bw/2, bh/2))
end