Repository navigation
Expand file tree
/
Copy pathpoint-cloud.rs
More file actions
144 lines (118 loc) · 3.64 KB
/
Copy pathpoint-cloud.rs
File metadata and controls
144 lines (118 loc) · 3.64 KB
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
use std::io::Write;
use std::path::PathBuf;
use clap::{Parser, ValueEnum};
use generative::io::get_output_writer;
use rand::rngs::StdRng;
use rand::{Rng, SeedableRng};
use rand_distr::{Binomial, Distribution, Uniform};
#[derive(Debug, Clone, ValueEnum)]
enum RandomDomain {
UnitSquare,
UnitCircle,
}
/// Generate random point clouds in a unit square or circle
#[derive(Debug, Parser)]
#[clap(name = "point-cloud")]
struct CmdlineOptions {
/// The log level
#[clap(long, default_value_t = tracing::Level::INFO)]
log_level: tracing::Level,
/// Output file to write result to. Defaults to stdout.
#[clap(short, long)]
output: Option<PathBuf>,
/// The random seed to use. Use zero to let the tool pick its own random seed.
#[clap(long, default_value = "0")]
seed: u64,
/// The number of points to generate.
#[clap(short, long)]
points: u64,
/// Generate a random number of points with mean '--points'
#[clap(short, long)]
random_number: bool,
/// The random domain to generate points inside.
#[clap(short, long, default_value = "unit-circle", value_enum)]
domain: RandomDomain,
/// Scale the generated points.
#[clap(short, long, default_value = "1.0")]
scale: f64,
}
struct Double2 {
x: f64,
y: f64,
}
fn generate(points: usize, domain: RandomDomain, rng: &mut StdRng) -> Vec<Double2> {
match domain {
RandomDomain::UnitSquare => generate_square(points, rng),
RandomDomain::UnitCircle => generate_circle(points, rng),
}
}
fn generate_square(points: usize, rng: &mut StdRng) -> Vec<Double2> {
let mut v = Vec::with_capacity(points);
let dist = Uniform::new(0.0, 1.0).unwrap();
for _ in 0..points {
let point = Double2 {
x: dist.sample(rng),
y: dist.sample(rng),
};
v.push(point);
}
v
}
fn generate_circle(points: usize, rng: &mut StdRng) -> Vec<Double2> {
let mut v = Vec::with_capacity(points);
let r_dist = Uniform::new(0.0, 1.0).unwrap();
let theta_dist = Uniform::new(0.0, 2.0 * std::f64::consts::PI).unwrap();
for _ in 0..points {
let r = r_dist.sample(rng);
let theta = theta_dist.sample(rng);
let point = Double2 {
x: r * theta.cos(),
y: r * theta.sin(),
};
v.push(point);
}
v
}
fn generate_random_seed_if_not_specified(seed: u64) -> u64 {
if seed == 0 {
let mut rng = rand::rng();
rng.random()
} else {
seed
}
}
fn main() -> eyre::Result<()> {
color_eyre::install()?;
let args = CmdlineOptions::parse();
let filter = tracing_subscriber::EnvFilter::builder()
.with_default_directive(args.log_level.into())
.from_env_lossy();
tracing_subscriber::fmt()
.with_env_filter(filter)
.with_ansi(true)
.with_writer(std::io::stderr)
.init();
let seed = generate_random_seed_if_not_specified(args.seed);
let mut rng = StdRng::seed_from_u64(seed);
let num_points = if args.random_number {
// close enough to normal with integers
let n = args.points * 2; // changes mean
let p = 0.5; // changes skew
let dist = Binomial::new(n, p)?;
dist.sample(&mut rng)
} else {
args.points
};
tracing::info!("Generating {num_points} points with seed {seed}");
let points = generate(num_points as usize, args.domain, &mut rng);
let mut writer = get_output_writer(&args.output)?;
for point in points {
writeln!(
writer,
"POINT ({} {})",
point.x * args.scale,
point.y * args.scale
)?;
}
Ok(())
}