// eccentricity: 0->circle, limit->1: line EllipseGenerator get_ellipse_generator(Eigen::Vector2f center_span, Eigen::Vector2f radius_span, float min_r_ratio, float min_arc_angle, float sigma) { std::uniform_real_distribution<float> center_dist(center_span(0), center_span(1)); std::uniform_real_distribution<float> radius_dist(radius_span(0), radius_span(1)); std::uniform_real_distribution<float> angle_dist(0, 2*M_PI); // Center. float cx = center_dist(G_engine); float cy = center_dist(G_engine); // Rotation. Doesn't make sense to rotate ellipse more than 180 deg. float angle = angle_dist(G_engine); if (angle > M_PI) angle -= M_PI; // Radii; ratio of minor/major radii must not be < min_r_ratio float a, b; do { a = radius_dist(G_engine); b = radius_dist(G_engine); if (a < b) std::swap(a, b); } while (b/a < min_r_ratio); // Arc span; must be at least min_arc_angle float phi_min, phi_max; do { phi_min = angle_dist(G_engine); phi_max = angle_dist(G_engine); if (phi_max < phi_min) std::swap(phi_max, phi_min); } while (phi_max - phi_min < min_arc_angle); EllipseGeometry geometry{Eigen::Vector2f(cx, cy), Eigen::Vector2f(a, b), angle}; return EllipseGenerator(geometry, Eigen::Vector2f(phi_min, phi_max), sigma); }
size_t poisson_square(tkernel<glm::tvec2<T, P>> & kernel, const T min_dist, const unsigned int num_probes) { assert(kernel.depth() == 1); std::random_device RD; std::mt19937_64 generator(RD()); std::uniform_real_distribution<> radius_dist(min_dist, min_dist * 2.0); std::uniform_real_distribution<> angle_dist(0.0, 2.0 * glm::pi<T>()); std::uniform_int_distribution<> int_distribute(0, std::numeric_limits<int>::max()); auto occupancy = poisson_square_map<T, P>{ min_dist }; size_t k = 0; // number of valid/final points within the kernel kernel[k] = glm::tvec2<T, P>(0.5, 0.5); auto actives = std::list<size_t>(); actives.push_back(k); occupancy.mask(kernel[k], k); while (!actives.empty() && k < kernel.size() - 1) { // randomly pick an active point const auto pick = int_distribute(generator); auto pick_it = actives.begin(); std::advance(pick_it, pick % actives.size()); const auto active = kernel[*pick_it]; std::vector<std::tuple<glm::tvec2<T, P>, T>> probes{ num_probes }; #pragma omp parallel for for (int i = 0; i < static_cast<int>(num_probes); ++i) { const auto r = radius_dist(generator); const auto a = angle_dist(generator); auto probe = glm::tvec2<T, P>{ active.x + r * cos(a), active.y + r * sin(a) }; // within square? (tilable) if (probe.x < 0.0) probe.x += 1.0; else if (probe.x >= 1.0) probe.x -= 1.0; if (probe.y < 0.0) probe.y += 1.0; else if (probe.y >= 1.0) probe.y -= 1.0; // Note: do NOT make this optimization //if (!tilable && (probe.x < 0.0 || probe.x > 1.0 || probe.y < 0.0 || probe.y > 1.0)) // continue; // points within min_dist? const auto masked = occupancy.masked(probe, kernel); const auto delta = glm::abs(active - probe); probes[i] = std::make_tuple<glm::tvec2<T, P>, T>(std::move(probe), (masked ? static_cast<T>(-1.0) : glm::dot(delta, delta))); } // pick nearest probe from sample set glm::vec2 nearest_probe; auto nearest_dist = 4 * min_dist * min_dist; auto nearest_found = false; for (int i = 0; i < static_cast<int>(num_probes); ++i) { // is this nearest point yet? - optimized by using square distance -> skipping sqrt const auto new_dist = std::get<1>(probes[i]); if (new_dist < 0.0 || nearest_dist < new_dist) continue; if (!nearest_found) nearest_found = true; nearest_dist = new_dist; nearest_probe = std::get<0>(probes[i]); } if (!nearest_found && (actives.size() > 0 || k > 1)) { actives.erase(pick_it); continue; } kernel[++k] = nearest_probe; actives.push_back(k); occupancy.mask(nearest_probe, k); } return k + 1; }