5#include <glm/gtc/constants.hpp>
11 SampleResult sample_random_volume(
const SamplerBounds&
b, Stochastic::Stochastic& rng)
14 rng(
b.min.x,
b.max.x),
15 rng(
b.min.y,
b.max.y),
18 const glm::vec3 ext =
b.extent();
19 return { .position = pos, .color = (pos -
b.min) / ext, .scalar = 0.5F };
22 SampleResult sample_random_surface(
const SamplerBounds&
b, Stochastic::Stochastic& rng)
24 static constexpr glm::vec3 k_face_colors[6] {
25 { 0.8F, 0.3F, 0.3F }, { 1.0F, 0.4F, 0.4F },
26 { 0.3F, 0.8F, 0.3F }, { 0.4F, 1.0F, 0.4F },
27 { 0.3F, 0.3F, 0.8F }, { 0.4F, 0.4F, 1.0F }
30 const int face =
static_cast<int>(rng(0, 6));
35 pos = {
b.min.x, rng(
b.min.y,
b.max.y), rng(
b.min.z,
b.max.z) };
38 pos = {
b.max.x, rng(
b.min.y,
b.max.y), rng(
b.min.z,
b.max.z) };
41 pos = { rng(
b.min.x,
b.max.x),
b.min.y, rng(
b.min.z,
b.max.z) };
44 pos = { rng(
b.min.x,
b.max.x),
b.max.y, rng(
b.min.z,
b.max.z) };
47 pos = { rng(
b.min.x,
b.max.x), rng(
b.min.y,
b.max.y),
b.min.z };
50 pos = { rng(
b.min.x,
b.max.x), rng(
b.min.y,
b.max.y),
b.max.z };
54 return { .position = pos, .color = k_face_colors[face], .scalar = 1.0F };
57 SampleResult sample_grid(
const SamplerBounds&
b,
size_t idx,
size_t total)
59 const size_t gs =
static_cast<size_t>(std::cbrt(
static_cast<double>(total))) + 1;
60 const glm::vec3 spacing =
b.extent() /
static_cast<float>(gs);
61 const size_t x = idx % gs;
62 const size_t y = (idx / gs) % gs;
63 const size_t z = idx / (gs * gs);
65 const glm::vec3 pos =
b.min + glm::vec3(
static_cast<float>(x) * spacing.x,
static_cast<float>(y) * spacing.y,
static_cast<float>(z) * spacing.z);
67 const glm::vec3 color {
68 static_cast<float>(x) /
static_cast<float>(gs),
69 static_cast<float>(y) /
static_cast<float>(gs),
70 static_cast<float>(z) /
static_cast<float>(gs)
73 return { .position = pos, .color = color, .scalar = 0.5F };
76 SampleResult sample_sphere_volume(
const SamplerBounds&
b, Stochastic::Stochastic& rng)
78 const float mr =
b.max_radius();
79 const float radius = mr * std::cbrt(
static_cast<float>(rng(0.0F, 1.0F)));
80 const auto theta =
static_cast<float>(rng(0.0F, glm::two_pi<double>()));
81 const float phi = std::acos(
static_cast<float>(rng(-1.0F, 1.0F)));
83 const glm::vec3 pos =
b.center() + glm::vec3(radius * std::sin(phi) * std::cos(theta), radius * std::sin(phi) * std::sin(theta), radius * std::cos(phi));
85 const float norm = radius / mr;
88 .color = glm::mix(glm::vec3(1.0F, 0.8F, 0.2F), glm::vec3(0.2F, 0.4F, 1.0F), norm),
93 SampleResult sample_sphere_surface(
const SamplerBounds&
b, Stochastic::Stochastic& rng)
95 const float radius =
b.max_radius();
96 const auto theta =
static_cast<float>(rng(0.0F, glm::two_pi<double>()));
97 const float phi = std::acos(
static_cast<float>(rng(-1.0F, 1.0F)));
99 const glm::vec3 pos =
b.center() + glm::vec3(radius * std::sin(phi) * std::cos(theta), radius * std::sin(phi) * std::sin(theta), radius * std::cos(phi));
101 const float lat = std::sin(phi);
104 .color = { (std::sin(theta) + 1.0F) * 0.5F, phi / glm::pi<float>(), (std::cos(theta) + 1.0F) * 0.5F },
109 SampleResult sample_uniform_grid(
const SamplerBounds&
b,
size_t idx,
size_t total)
111 const auto ppa =
static_cast<size_t>(std::cbrt(
static_cast<double>(total)));
112 const glm::vec3 step =
b.extent() /
static_cast<float>(ppa - 1 > 0 ? ppa - 1 : 1);
113 const size_t x = idx % ppa;
114 const size_t y = (idx / ppa) % ppa;
115 const size_t z = idx / (ppa * ppa);
117 const glm::vec3 pos =
b.min + glm::vec3(
static_cast<float>(x) * step.x,
static_cast<float>(y) * step.y,
static_cast<float>(z) * step.z);
119 const glm::vec3 color {
120 static_cast<float>(x) /
static_cast<float>(ppa - 1 > 0 ? ppa - 1 : 1),
121 static_cast<float>(y) / static_cast<float>(ppa - 1 > 0 ? ppa - 1 : 1),
122 static_cast<float>(z) / static_cast<float>(ppa - 1 > 0 ? ppa - 1 : 1)
125 const float t = glm::length(pos -
b.center()) /
b.max_radius();
126 return { .position = pos, .color = color, .scalar = t };
129 SampleResult sample_random_sphere(
const SamplerBounds&
b, Stochastic::Stochastic& rng)
131 const auto theta =
static_cast<float>(rng(0.0F, glm::two_pi<double>()));
132 const float phi = std::acos(
static_cast<float>(rng(-1.0F, 1.0F)));
133 const float radius =
b.max_radius() *
static_cast<float>(std::cbrt(rng(0.0F, 1.0F)));
135 const glm::vec3 pos =
b.center() + radius * glm::vec3(std::sin(phi) * std::cos(theta), std::sin(phi) * std::sin(theta), std::cos(phi));
139 .color = { radius /
b.max_radius(), theta / glm::two_pi<float>(), phi / glm::pi<float>() },
140 .scalar = radius /
b.max_radius()
144 SampleResult sample_random_cube(
const SamplerBounds&
b, Stochastic::Stochastic& rng)
146 const glm::vec3 pos {
147 rng(
b.min.x,
b.max.x),
148 rng(
b.min.y,
b.max.y),
149 rng(
b.min.z,
b.max.z)
151 return { .position = pos, .color = (pos -
b.min) /
b.extent(), .scalar = 0.5F };
154 std::vector<SampleResult> sample_perlin_field(
155 const SamplerBounds&
b,
157 Stochastic::Stochastic& rng)
160 std::vector<SampleResult> out;
163 while (out.size() <
count) {
165 rng(
b.min.x,
b.max.x),
166 rng(
b.min.y,
b.max.y),
167 rng(
b.min.z,
b.max.z)
169 if (
perlin.
at(p.x, p.y, p.z) > rng(0.0, 1.0)) {
170 out.push_back({ .position = p, .color = (p -
b.min) /
b.extent(), .scalar = 0.5F });
177 std::vector<SampleResult> sample_brownian_path(
178 const SamplerBounds&
b,
180 Stochastic::Stochastic& rng)
182 auto alg_backup = rng.get_algorithm();
185 std::vector<SampleResult> out;
188 glm::vec3 pos =
b.center();
189 for (
size_t i = 0; i <
count; ++i) {
190 pos += glm::vec3(rng(-1.0, 1.0), rng(-1.0, 1.0), rng(-1.0, 1.0)) * 0.1F;
191 pos = glm::clamp(pos,
b.min,
b.max);
192 out.push_back({ .position = pos,
193 .color = glm::vec3(
static_cast<float>(i) /
static_cast<float>(
count)),
194 .scalar =
static_cast<float>(i) /
static_cast<float>(
count) });
196 rng.set_algorithm(alg_backup);
201 std::vector<SampleResult> sample_stratified_cube(
202 const SamplerBounds&
b,
204 Stochastic::Stochastic& rng)
206 const auto ppa =
static_cast<size_t>(std::cbrt(
count));
207 const glm::vec3 step =
b.extent() /
static_cast<float>(ppa);
208 std::vector<SampleResult> out;
209 out.reserve(ppa * ppa * ppa);
211 for (
size_t x = 0; x < ppa; ++x) {
212 for (
size_t y = 0; y < ppa; ++y) {
213 for (
size_t z = 0; z < ppa; ++z) {
214 const glm::vec3 jitter { rng(-0.5F, 0.5F), rng(-0.5F, 0.5F), rng(-0.5F, 0.5F) };
215 const glm::vec3 pos =
b.min + (glm::vec3(x, y, z) + 0.5F + jitter) * step;
216 out.push_back({ .position = pos, .color = (pos -
b.min) /
b.extent(), .scalar = 0.6F });
224 std::vector<SampleResult> sample_spline_path(
225 const SamplerBounds&
b,
227 Stochastic::Stochastic& rng)
229 Eigen::MatrixXd ctrl(3, 6);
230 for (
int i = 0; i < 6; ++i) {
231 ctrl.col(i) = Eigen::Vector3d(
232 rng(
b.min.x,
b.max.x),
233 rng(
b.min.y,
b.max.y),
234 rng(
b.min.z,
b.max.z));
239 std::vector<SampleResult> out;
240 out.reserve(path.cols());
241 for (Eigen::Index i = 0; i < path.cols(); ++i) {
242 const glm::vec3 pos(path(0, i), path(1, i), path(2, i));
243 out.push_back({ .position = pos, .color = glm::vec3(0.1F, 0.8F, 0.4F), .scalar = 0.5F });
249 std::vector<SampleResult> sample_fibonacci_sphere(
const SamplerBounds&
b,
size_t count)
251 const float phi = glm::pi<float>() * (3.0F - std::sqrt(5.0F));
252 const float mr =
b.max_radius();
253 const glm::vec3 ext =
b.extent();
254 std::vector<SampleResult> out;
257 for (
size_t i = 0; i <
count; ++i) {
258 const float y = 1.0F - (
static_cast<float>(i) /
static_cast<float>(
count - 1)) * 2.0F;
259 const float radius = std::sqrt(1.0F - y * y);
260 const float theta = phi *
static_cast<float>(i);
261 const glm::vec3 pos =
b.center() + mr * glm::vec3(std::cos(theta) * radius, y, std::sin(theta) * radius);
262 out.push_back({ .position = pos, .color = (pos -
b.min) / ext, .scalar = 1.0F });
268 std::vector<SampleResult> sample_fibonacci_spiral(
const SamplerBounds&
b,
size_t count)
270 const float golden_angle = glm::pi<float>() * (3.0F - std::sqrt(5.0F));
271 const float mr =
b.max_radius();
272 std::vector<SampleResult> out;
275 for (
size_t i = 0; i <
count; ++i) {
276 const float r = mr * std::sqrt(
static_cast<float>(i) /
static_cast<float>(
count));
277 const float theta =
static_cast<float>(i) * golden_angle;
278 const glm::vec3 pos =
b.center() + glm::vec3(r * std::cos(theta), r * std::sin(theta), 0.0F);
279 out.push_back({ .position = pos,
280 .color = { r / mr, 0.5F, 1.0F - r / mr },
287 std::vector<SampleResult> sample_lissajous(
const SamplerBounds&
b,
size_t count)
289 static constexpr float a = 3.0F, bv = 2.0F, c = 5.0F;
290 const float mr =
b.max_radius();
291 std::vector<SampleResult> out;
294 for (
size_t i = 0; i <
count; ++i) {
295 const float t = (
static_cast<float>(i) /
static_cast<float>(
count)) * glm::two_pi<float>() * 2.0F;
296 const glm::vec3 pos =
b.center() + mr * glm::vec3(std::sin(
a * t), std::sin(bv * t), std::sin(c * t));
297 out.push_back({ .position = pos,
298 .color = { 0.5F + pos.x, 0.5F, 0.8F },
305 std::vector<SampleResult> sample_torus(
const SamplerBounds&
b,
size_t count)
307 const float mr =
b.max_radius();
308 const float main_r = mr * 0.7F;
309 const float tube_r = mr * 0.3F;
310 std::vector<SampleResult> out;
313 for (
size_t i = 0; i <
count; ++i) {
314 const float u = (
static_cast<float>(i) /
static_cast<float>(
count)) * glm::two_pi<float>();
315 const float v = (
static_cast<float>(i * 7 %
count) /
static_cast<float>(
count)) * glm::two_pi<float>();
316 const glm::vec3 pos =
b.center() + glm::vec3((main_r + tube_r * std::cos(v)) * std::cos(u), (main_r + tube_r * std::cos(v)) * std::sin(u), tube_r * std::sin(v));
317 const glm::vec3 color { (std::cos(u) + 1.0F) * 0.5F, (std::cos(v) + 1.0F) * 0.5F, (std::sin(u) + 1.0F) * 0.5F };
318 out.push_back({ .position = pos, .color = color, .scalar = (std::cos(v) + 1.0F) * 0.5F });
340 return sample_perlin_field(bounds,
count, rng);
342 return sample_brownian_path(bounds,
count, rng);
344 return sample_stratified_cube(bounds,
count, rng);
346 return sample_spline_path(bounds,
count, rng);
348 return sample_fibonacci_sphere(bounds,
count);
350 return sample_fibonacci_spiral(bounds,
count);
352 return sample_torus(bounds,
count);
354 return sample_lissajous(bounds,
count);
357 std::vector<SampleResult> out;
359 for (
size_t i = 0; i <
count; ++i) {
376 return sample_random_volume(bounds, rng);
378 return sample_random_surface(bounds, rng);
380 return sample_grid(bounds, index, total);
382 return sample_sphere_volume(bounds, rng);
384 return sample_sphere_surface(bounds, rng);
386 return sample_uniform_grid(bounds, index, total);
388 return sample_random_sphere(bounds, rng);
390 return sample_random_cube(bounds, rng);
392 return { .position = glm::vec3(0.0F), .color = glm::vec3(0.5F), .scalar = 0.5F };
397 std::span<const SampleResult> samples,
398 glm::vec2 size_range)
400 std::vector<Nodes::PointVertex> out;
401 out.reserve(samples.size());
402 for (
const auto& s : samples) {
409 std::span<const SampleResult> samples,
410 glm::vec2 thickness_range)
412 std::vector<Nodes::LineVertex> out;
413 out.reserve(samples.size());
414 for (
const auto& s : samples) {
double at(double x, double y=0.0, double z=0.0)
Multi-dimensional generation (Perlin, spatial noise)
Unified generative infrastructure for stochastic and procedural algorithms.
Stochastic perlin(int octaves=4, double persistence=0.5)
Creates Perlin noise generator.
SampleResult generate_sample_at(SpatialDistribution dist, size_t index, size_t total, const SamplerBounds &bounds, Stochastic::Stochastic &rng)
Generate a single sample at a specific index (for indexed/sequential modes).
Nodes::PointVertex to_point_vertex(const SampleResult &s, glm::vec2 size_range={ 8.0F, 12.0F }) noexcept
Project SampleResult to PointVertex.
Eigen::MatrixXd generate_interpolated_points(const Eigen::MatrixXd &control_points, Eigen::Index num_samples, InterpolationMode mode, double tension)
Generate interpolated points from control points.
std::vector< Nodes::PointVertex > to_point_vertices(std::span< const SampleResult > samples, glm::vec2 size_range)
Batch-project SampleResult vector to PointVertex.
std::vector< Nodes::LineVertex > to_line_vertices(std::span< const SampleResult > samples, glm::vec2 thickness_range)
Batch-project SampleResult vector to LineVertex.
SpatialDistribution
Spatial distribution mode for point cloud and particle generation.
Nodes::LineVertex to_line_vertex(const SampleResult &s, glm::vec2 thickness_range={ 1.0F, 2.0F }) noexcept
Project SampleResult to LineVertex.
std::vector< SampleResult > generate_samples(SpatialDistribution dist, size_t count, const SamplerBounds &bounds, Stochastic::Stochastic &rng)
Generate a batch of spatially distributed samples.
Position and normalised color derived from spatial sampling.
Spatial domain for vertex generation.