File: pj.h

package info (click to toggle)
r-cran-spacefillr 0.4.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 10,472 kB
  • sloc: ansic: 32,867; cpp: 5,657; makefile: 2
file content (106 lines) | stat: -rwxr-xr-x 3,046 bytes parent folder | download
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
/*
 * Copyright (C) Andrew Helmer 2020.
 * Licensed under MIT Open-Source License: see LICENSE.
 *
 * Implements the most basic algorithm from Christensen et al., Progressive
 * Jittered Sampling. There's really no reason to use this, but the code could
 * be instructional.
 */
#ifndef SAMPLE_GENERATION_PJ_H_
#define SAMPLE_GENERATION_PJ_H_

#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <memory>
#include <random>
#include <utility>
#include <vector>

#include "util.h"
#include "rng.h"

namespace pmj {

// Progressive jittered samples shouldn't really be used, it's more just a
// learning example.
std::unique_ptr<pmj::Point[]> GetProgJitteredSamples(
    const int num_samples, random_gen& rng);

Point RandomSample(
        double min_x, double max_x, double min_y, double max_y, random_gen& rng) {
    return {UniformRand(min_x, max_x, rng), UniformRand(min_y, max_y, rng)};
}

Point GetSample(
        const int x_pos, const int y_pos, const double grid_size, random_gen& rng) {
    return RandomSample(x_pos*grid_size, (x_pos+1)*grid_size,
                        y_pos*grid_size, (y_pos+1)*grid_size, rng);
}

void GenerateSamplesForQuadrant(
        const Point& sample,
        const int num_samples,
        const int n,
        const int i,
        const int x_pos,
        const int y_pos,
        const double grid_size,
        Point* samples,
        random_gen& rng) {
    // Generate diagonally opposite.
    samples[n+i] = GetSample(x_pos ^ 1, y_pos ^ 1, grid_size, rng);

    if (2*n+i >= num_samples) {
        return;
    }

    // Pick one of the two adjacent cells to generate new sample.
    int new_x_pos = x_pos;
    int new_y_pos = y_pos;
    if (UniformRand(0, 1, rng) < 0.5) {
        new_x_pos = x_pos ^ 1;
    } else {
        new_y_pos = y_pos ^ 1;
    }
    samples[2*n+i] = GetSample(new_x_pos, new_y_pos, grid_size, rng);

    // Generate a sample in the diagonal of the previous cell.
    if (3*n+i >= num_samples) {
        return;
    }

    samples[3*n+i] = GetSample(new_x_pos ^ 1, new_y_pos ^ 1, grid_size, rng);
}

std::unique_ptr<Point[]> GetProgJitteredSamples(
        const int num_samples, random_gen& rng) {
    auto samples = std::unique_ptr<Point[]>(new Point[num_samples]());

    // Generate first sample randomly.
    samples[0] = RandomSample(0, 1, 0, 1, rng);

    int n = 1;  // Number of samples in previous pass.
    int dim = 2;  // The number of subquadrants in one dimension.
    double grid_size = 0.5;  // The subquadrant size in one dimension, 1.0 / dim.
    while (n < num_samples) {
        for (int i = 0; i < n && n+i < num_samples; i++) {
            const auto& sample = samples[i];

            int x_pos = sample.x * dim;
            int y_pos = sample.y * dim;

            GenerateSamplesForQuadrant(
                sample, num_samples, n, i, x_pos, y_pos, grid_size, samples.get(), rng);
        }
        n *= 4;
        dim *= 2;
        grid_size *= 0.5;
    }

    return samples;
}

} //namespace pmj

#endif  // SAMPLE_GENERATION_PJ_H_