Skip to content

Commit 03c0c37

Browse files
authored
Add worley noise optional feature (#74)
* worley_noise * a simple fix, forgot to ad dmsort as a necessary package * replaces .unwrap() with ? * forgot to save :cryingintoheavens: * IM SORRY I CANT USE ? IN CLOSURES * Completely reworks how the code works, massively optimizing it. Generation lost a tiny bit of it's fidelity but oh well, looks fantastic rn * one last change * tfw errors * makes the noise generate pretties noise * cargo fmt
1 parent f6b2172 commit 03c0c37

File tree

5 files changed

+241
-1
lines changed

5 files changed

+241
-1
lines changed

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ mysql = { version = "20.0", optional = true }
4141
dashmap = { version = "4.0", optional = true }
4242
zip = { version = "0.5.8", optional = true }
4343
rand = {version = "0.8", optional = true}
44-
44+
dmsort = {version = "1.0.0", optional = true}
4545

4646
[features]
4747
default = ["cellularnoise", "dmi", "file", "git", "http", "json", "log", "noise", "sql", "url"]
@@ -60,6 +60,7 @@ url = ["url-dep", "percent-encoding"]
6060
# non-default features
6161
hash = ["base64", "const-random", "md-5", "hex", "sha-1", "sha2", "twox-hash"]
6262
unzip = ["zip", "jobs"]
63+
worleynoise = ["rand","dmsort"]
6364

6465
# internal feature-like things
6566
jobs = ["flume"]

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ Additional features are:
9393
* hash: Faster replacement for `md5`, support for SHA-1, SHA-256, and SHA-512. Requires OpenSSL on Linux.
9494
* url: Faster replacements for `url_encode` and `url_decode`.
9595
* unzip: Function to download a .zip from a URL and unzip it to a directory.
96+
* worleynoise: Function that generates a type of nice looking cellular noise, more expensive than cellularnoise
9697

9798
## Installing
9899

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ pub mod sql;
3030
pub mod unzip;
3131
#[cfg(feature = "url")]
3232
pub mod url;
33+
#[cfg(feature = "worleynoise")]
34+
pub mod worleynoise;
3335

3436
#[cfg(not(target_pointer_width = "32"))]
3537
compile_error!("rust-g must be compiled for a 32-bit target");

src/worleynoise.rs

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
use crate::error::Result;
2+
use rand::*;
3+
use std::fmt::Write;
4+
use std::rc::Rc;
5+
6+
byond_fn! { worley_generate(region_size, threshold, node_per_region_chance, width, height) {
7+
worley_noise(region_size, threshold, node_per_region_chance, width, height).ok()
8+
} }
9+
10+
// This is a quite complex algorithm basically what it does is it creates 2 maps, one filled with cells and the other with 'regions' that map onto these cells.
11+
// Each region can spawn 1 node, the cell then determines wether it is true or false depending on the distance from it to the nearest node in the region minus the second closest node.
12+
// If this distance is greater than the threshold then the cell is true, otherwise it is false.
13+
fn worley_noise(
14+
str_reg_size: &str,
15+
str_positive_threshold: &str,
16+
str_node_per_region_chance: &str,
17+
str_width: &str,
18+
str_height: &str,
19+
) -> Result<String> {
20+
let region_size = str_reg_size.parse::<i32>()?;
21+
let positive_threshold = str_positive_threshold.parse::<f32>()?;
22+
let width = str_width.parse::<i32>()?;
23+
let height = str_height.parse::<i32>()?;
24+
let node_per_region_chance = str_node_per_region_chance.parse::<f32>()?;
25+
26+
//i fucking mixed up width and height again. it really doesnt matter but here is a comment just warning you.
27+
let mut map = Map::new(region_size, height, width, node_per_region_chance);
28+
29+
map.generate_noise(positive_threshold as f32);
30+
31+
let mut output = String::new();
32+
33+
for row in map.cell_map {
34+
for cell in row {
35+
if cell.value {
36+
let _ = write!(output, "1");
37+
} else {
38+
let _ = write!(output, "0");
39+
}
40+
}
41+
}
42+
Ok(output)
43+
}
44+
45+
struct Map {
46+
region_size: i32,
47+
region_map: Vec<Vec<Rc<Region>>>,
48+
cell_map: Vec<Vec<Cell>>,
49+
cell_map_width: i32,
50+
cell_map_height: i32,
51+
node_chance: f32,
52+
}
53+
54+
impl Map {
55+
fn new(region_size: i32, cell_map_width: i32, cell_map_height: i32, node_chance: f32) -> Map {
56+
let mut map = Map {
57+
region_size: region_size,
58+
region_map: Vec::new(),
59+
cell_map: Vec::new(),
60+
cell_map_width: cell_map_width,
61+
cell_map_height: cell_map_height,
62+
node_chance: node_chance,
63+
};
64+
65+
map.init_regions();
66+
67+
for x in 0..cell_map_width {
68+
map.cell_map.push(Vec::new());
69+
for y in 0..cell_map_height {
70+
let cell = Cell::new(
71+
x,
72+
y,
73+
map.region_map[(x / region_size) as usize][(y / region_size) as usize].clone(),
74+
);
75+
map.cell_map[(x) as usize].push(cell);
76+
}
77+
}
78+
map
79+
}
80+
fn init_regions(&mut self) {
81+
let mut rng = rand::thread_rng();
82+
83+
let regions_x = self.cell_map_width / self.region_size;
84+
let regions_y = self.cell_map_height / self.region_size;
85+
//those two variables ensure that we dont EVER panic due to not having enough nodes spawned for the distance algorithm to function.
86+
let mut node_count = 0;
87+
let mut distance_in_regions_since_last_node = 0;
88+
89+
for i in 0..regions_x {
90+
distance_in_regions_since_last_node += 1;
91+
self.region_map.push(Vec::new());
92+
for j in 0..regions_y {
93+
distance_in_regions_since_last_node += 1;
94+
let mut region = Region::new(i, j);
95+
if rng.gen_range(0..100) as f32 <= self.node_chance || node_count < 2 {
96+
let xcord = rng.gen_range(0..self.region_size);
97+
let ycord = rng.gen_range(0..self.region_size);
98+
let node =
99+
Node::new(xcord + i * self.region_size, ycord + j * self.region_size);
100+
region.node = Some(node);
101+
node_count += 1;
102+
distance_in_regions_since_last_node = 0;
103+
}
104+
105+
if distance_in_regions_since_last_node > 3 {
106+
node_count = 0;
107+
}
108+
109+
let rcregion = Rc::new(region);
110+
111+
self.region_map[i as usize].push(rcregion);
112+
}
113+
}
114+
}
115+
116+
fn get_regions_in_bound(&self, x: i32, y: i32, radius: i32) -> Vec<&Region> {
117+
let mut regions = Vec::new();
118+
let x_min = x - radius;
119+
let x_max = x + radius;
120+
let y_min = y - radius;
121+
let y_max = y + radius;
122+
for i in x_min..x_max {
123+
for j in y_min..y_max {
124+
let region_x = i;
125+
let region_y = j;
126+
if region_x >= 0
127+
&& region_x < self.region_map.len() as i32
128+
&& region_y >= 0
129+
&& region_y < self.region_map[region_x as usize].len() as i32
130+
{
131+
let region = &self.region_map[region_x as usize][region_y as usize];
132+
regions.push(region.as_ref());
133+
}
134+
}
135+
}
136+
regions
137+
}
138+
139+
fn generate_noise(&mut self, threshold: f32) {
140+
for i in 0..self.cell_map.len() {
141+
for j in 0..self.cell_map[i as usize].len() {
142+
let cell = &self.cell_map[i as usize][j as usize];
143+
let region = &self.region_map[cell.region.as_ref().reg_x as usize]
144+
[cell.region.as_ref().reg_y as usize];
145+
let neighbours = self.get_regions_in_bound(region.reg_x, region.reg_y, 3);
146+
147+
let mut node_vec = Vec::new();
148+
for neighbour in neighbours {
149+
if neighbour.node.is_some() {
150+
let node = neighbour.node.as_ref().unwrap();
151+
node_vec.push(node);
152+
}
153+
}
154+
155+
dmsort::sort_by(&mut node_vec, |a, b| {
156+
quick_distance_from_to(cell.x, cell.y, a.x, a.y)
157+
.partial_cmp(&quick_distance_from_to(cell.x, cell.y, b.x, b.y))
158+
.unwrap()
159+
});
160+
let dist = distance_from_to(cell.x, cell.y, node_vec[0].x, node_vec[0].y)
161+
- distance_from_to(cell.x, cell.y, node_vec[1].x, node_vec[1].y);
162+
//
163+
let mutable_cell = &mut self.cell_map[i as usize][j as usize];
164+
if dist.abs() > threshold {
165+
mutable_cell.value = true;
166+
}
167+
}
168+
}
169+
}
170+
}
171+
172+
fn distance_from_to(x1: i32, y1: i32, x2: i32, y2: i32) -> f32 {
173+
let x_diff = x1 - x2;
174+
let y_diff = y1 - y2;
175+
let distance = (((x_diff * x_diff) + (y_diff * y_diff)) as f32).sqrt();
176+
distance
177+
}
178+
179+
fn quick_distance_from_to(x1: i32, y1: i32, x2: i32, y2: i32) -> f32 {
180+
let x_diff = x1 - x2;
181+
let y_diff = y1 - y2;
182+
let distance = (x_diff.abs() + y_diff.abs()) as f32;
183+
distance
184+
}
185+
186+
struct Cell {
187+
x: i32,
188+
y: i32,
189+
value: bool,
190+
region: Rc<Region>,
191+
}
192+
193+
impl Cell {
194+
fn new(x: i32, y: i32, region: Rc<Region>) -> Cell {
195+
Cell {
196+
x: x,
197+
y: y,
198+
value: false,
199+
region: region,
200+
}
201+
}
202+
}
203+
204+
struct Region {
205+
reg_x: i32,
206+
reg_y: i32,
207+
node: Option<Node>,
208+
}
209+
210+
impl Region {
211+
fn new(reg_x: i32, reg_y: i32) -> Region {
212+
Region {
213+
reg_x: reg_x,
214+
reg_y: reg_y,
215+
node: None,
216+
}
217+
}
218+
}
219+
220+
struct Node {
221+
x: i32,
222+
y: i32,
223+
}
224+
225+
impl Node {
226+
fn new(x: i32, y: i32) -> Node {
227+
Node { x: x, y: y }
228+
}
229+
}

0 commit comments

Comments
 (0)