|
1 | 1 | use super::poisson_disk::poisson_disk_sample;
|
2 | 2 | use crate::vector::misc::dvec2_to_point;
|
3 | 3 | use glam::DVec2;
|
4 |
| -use kurbo::{Affine, BezPath, Line, ParamCurve, ParamCurveDeriv, PathSeg, Point, Rect, Shape}; |
| 4 | +use kurbo::{BezPath, Line, ParamCurve, ParamCurveDeriv, PathSeg, Point, Rect, Shape}; |
5 | 5 |
|
6 | 6 | /// Accuracy to find the position on [kurbo::Bezpath].
|
7 | 7 | const POSITION_ACCURACY: f64 = 1e-5;
|
@@ -198,77 +198,63 @@ fn bezpath_t_value_to_parametric(bezpath: &kurbo::BezPath, t: BezPathTValue, pre
|
198 | 198 | ///
|
199 | 199 | /// While the conceptual process described above asymptotically slows down and is never guaranteed to produce a maximal set in finite time,
|
200 | 200 | /// this is implemented with an algorithm that produces a maximal set in O(n) time. The slowest part is actually checking if points are inside the subpath shape.
|
201 |
| -pub fn poisson_disk_points(bezpath: &BezPath, separation_disk_diameter: f64, rng: impl FnMut() -> f64, subpaths: &[(BezPath, Rect)], subpath_index: usize) -> Vec<DVec2> { |
202 |
| - if bezpath.elements().is_empty() { |
| 201 | +pub fn poisson_disk_points(bezpath_index: usize, bezpaths: &[(BezPath, Rect)], separation_disk_diameter: f64, rng: impl FnMut() -> f64) -> Vec<DVec2> { |
| 202 | + let (this_bezpath, this_bbox) = bezpaths[bezpath_index].clone(); |
| 203 | + |
| 204 | + if this_bezpath.elements().is_empty() { |
203 | 205 | return Vec::new();
|
204 | 206 | }
|
205 |
| - let bbox = bezpath.bounding_box(); |
206 |
| - let (offset_x, offset_y) = (bbox.x0, bbox.y0); |
207 |
| - let (width, height) = (bbox.x1 - bbox.x0, bbox.y1 - bbox.y0); |
208 | 207 |
|
209 |
| - // TODO: Optimize the following code and make it more robust |
| 208 | + let offset = DVec2::new(this_bbox.x0, this_bbox.y0); |
| 209 | + let (width, height) = (this_bbox.width(), this_bbox.height()); |
210 | 210 |
|
211 |
| - let mut shape = bezpath.clone(); |
212 |
| - shape.close_path(); |
213 |
| - shape.apply_affine(Affine::translate((-offset_x, -offset_y))); |
| 211 | + // TODO: Optimize the following code and make it more robust |
214 | 212 |
|
215 | 213 | let point_in_shape_checker = |point: DVec2| {
|
216 | 214 | // Check against all paths the point is contained in to compute the correct winding number
|
217 | 215 | let mut number = 0;
|
218 |
| - for (i, (shape, bbox)) in subpaths.iter().enumerate() { |
219 |
| - let point = point + DVec2::new(bbox.x0, bbox.y0); |
| 216 | + |
| 217 | + for (i, (shape, bbox)) in bezpaths.iter().enumerate() { |
| 218 | + let point = point + offset; |
| 219 | + |
220 | 220 | if bbox.x0 > point.x || bbox.y0 > point.y || bbox.x1 < point.x || bbox.y1 < point.y {
|
221 | 221 | continue;
|
222 | 222 | }
|
223 |
| - let winding = shape.winding(dvec2_to_point(point)); |
224 | 223 |
|
225 |
| - if i == subpath_index && winding == 0 { |
| 224 | + let winding = shape.winding(dvec2_to_point(point)); |
| 225 | + if winding == 0 && i == bezpath_index { |
226 | 226 | return false;
|
227 | 227 | }
|
228 | 228 | number += winding;
|
229 | 229 | }
|
| 230 | + |
| 231 | + // Non-zero fill rule |
230 | 232 | number != 0
|
231 | 233 | };
|
232 | 234 |
|
233 | 235 | let square_edges_intersect_shape_checker = |position: DVec2, size: f64| {
|
234 |
| - let rect = Rect::new(position.x, position.y, position.x + size, position.y + size); |
235 |
| - bezpath_rectangle_intersections_exist(bezpath, rect) |
236 |
| - }; |
| 236 | + let min = position + offset; |
| 237 | + let max = min + DVec2::splat(size); |
237 | 238 |
|
238 |
| - let mut points = poisson_disk_sample(width, height, separation_disk_diameter, point_in_shape_checker, square_edges_intersect_shape_checker, rng); |
239 |
| - for point in &mut points { |
240 |
| - point.x += offset_x; |
241 |
| - point.y += offset_y; |
242 |
| - } |
243 |
| - points |
244 |
| -} |
245 |
| - |
246 |
| -fn bezpath_rectangle_intersections_exist(bezpath: &BezPath, rect: Rect) -> bool { |
247 |
| - if !bezpath.bounding_box().overlaps(rect) { |
248 |
| - return false; |
249 |
| - } |
| 239 | + let top_line = Line::new((min.x, min.y), (max.x, min.y)); |
| 240 | + let right_line = Line::new((max.x, min.y), (max.x, max.y)); |
| 241 | + let bottom_line = Line::new((max.x, max.y), (min.x, max.y)); |
| 242 | + let left_line = Line::new((min.x, max.y), (min.x, min.y)); |
250 | 243 |
|
251 |
| - // Top left |
252 |
| - let p1 = Point::new(rect.x0, rect.y0); |
253 |
| - // Top right |
254 |
| - let p2 = Point::new(rect.x1, rect.y0); |
255 |
| - // Bottom right |
256 |
| - let p3 = Point::new(rect.x1, rect.y1); |
257 |
| - // Bottom left |
258 |
| - let p4 = Point::new(rect.x0, rect.y1); |
259 |
| - |
260 |
| - let top_line = Line::new((p1.x, p1.y), (p2.x, p2.y)); |
261 |
| - let right_line = Line::new((p2.x, p2.y), (p3.x, p3.y)); |
262 |
| - let bottom_line = Line::new((p3.x, p3.y), (p4.x, p4.y)); |
263 |
| - let left_line = Line::new((p4.x, p4.y), (p1.x, p1.y)); |
264 |
| - |
265 |
| - for segment in bezpath.segments() { |
266 | 244 | for line in [top_line, right_line, bottom_line, left_line] {
|
267 |
| - if !segment.intersect_line(line).is_empty() { |
268 |
| - return true; |
| 245 | + for segment in this_bezpath.segments() { |
| 246 | + if !segment.intersect_line(line).is_empty() { |
| 247 | + return true; |
| 248 | + } |
269 | 249 | }
|
270 | 250 | }
|
271 |
| - } |
272 | 251 |
|
273 |
| - false |
| 252 | + false |
| 253 | + }; |
| 254 | + |
| 255 | + let mut points = poisson_disk_sample(width, height, separation_disk_diameter, point_in_shape_checker, square_edges_intersect_shape_checker, rng); |
| 256 | + for point in &mut points { |
| 257 | + *point += offset; |
| 258 | + } |
| 259 | + points |
274 | 260 | }
|
0 commit comments