···3131 let red_circle_in =
3232 Region::from_topright(draw_in.topright().translated(-3, 0), (4, 3)).unwrap();
33333434- let red_circle_at = red_circle_in.random_point_within();
3434+ let red_circle_at = red_circle_in.random_point();
35353636 let red_dot_layer = canvas.new_layer("red dot");
3737 let mut red_dot_friends = Layer::new("red dot friends");
-21
src/geometry/region.rs
···2525 self.iter().filter(|Point(x, y)| x >= y)
2626 }
27272828- pub fn random_point_within(&self) -> Point {
2929- Point::from(self.random_coordinates_within())
3030- }
3131-3232- pub fn random_point_within_except(&self, except: &Region) -> Point {
3333- // XXX this is probably not a good idea lmao
3434- loop {
3535- let point = self.random_point_within();
3636- if !except.contains(&point) {
3737- return point;
3838- }
3939- }
4040- }
4141-4228 pub fn ensure_nonempty(&self) -> Result<()> {
4329 if self.width() == 0 || self.height() == 0 {
4430 return Err(format_err!("Region {} is empty", self));
···172158 } else {
173159 self
174160 }
175175- }
176176-177177- pub fn random_coordinates_within(&self) -> (i32, i32) {
178178- (
179179- rand::thread_rng().gen_range(self.start.0..self.end.0) as i32,
180180- rand::thread_rng().gen_range(self.start.1..self.end.1) as i32,
181181- )
182161 }
183162184163 pub fn from_origin(end: Point) -> Result<Self> {
-189
src/graphics/canvas.rs
···218218 Ok(())
219219 }
220220221221- pub fn random_layer(&self, name: &str) -> Layer {
222222- self.random_layer_within(name, &self.world_region)
223223- }
224224-225225- pub fn random_object(&self) -> Object {
226226- self.random_object_within(&self.world_region)
227227- }
228228-229221 pub fn add_or_replace_layer(&mut self, layer: Layer) {
230222 if let Some(existing_layer) = self.layer_safe(&layer.name) {
231223 existing_layer.replace(layer);
···234226 }
235227 }
236228237237- pub fn random_layer_within(&self, name: &str, region: &Region) -> Layer {
238238- let mut objects: HashMap<String, ColoredObject> = HashMap::new();
239239- let number_of_objects = rand::thread_rng().gen_range(self.objects_count_range.clone());
240240- for i in 0..number_of_objects {
241241- let object = self.random_object_within(region);
242242- let hatchable = object.hatchable();
243243- objects.insert(
244244- format!("{}#{}", name, i),
245245- object.color(if hatchable {
246246- Fill::random_hatches(self.background)
247247- } else {
248248- Fill::random_solid(self.background)
249249- }),
250250- );
251251- }
252252- Layer {
253253- object_sizes: self.object_sizes,
254254- name: name.to_string(),
255255- objects,
256256- _render_cache: None,
257257- hidden: false,
258258- }
259259- }
260260-261261- pub fn random_linelikes(&self, layer_name: &str) -> Layer {
262262- self.random_linelikes_within(layer_name, &self.world_region)
263263- }
264264-265265- pub fn n_random_linelikes_within(
266266- &self,
267267- layer_name: &str,
268268- region: &Region,
269269- count: usize,
270270- ) -> Layer {
271271- let mut objects: HashMap<String, ColoredObject> = HashMap::new();
272272- for i in 0..count {
273273- let object = self.random_linelike_within(region);
274274- let hatchable = object.fillable();
275275- objects.insert(
276276- format!("{}#{}", layer_name, i),
277277- ColoredObject::from((
278278- object,
279279- if rand::thread_rng().gen_bool(0.5) {
280280- Some(Fill::random_solid(self.background))
281281- } else {
282282- None
283283- },
284284- )),
285285- );
286286- }
287287- Layer {
288288- object_sizes: self.object_sizes,
289289- name: layer_name.to_owned(),
290290- objects,
291291- _render_cache: None,
292292- hidden: false,
293293- }
294294- }
295295-296296- pub fn random_linelikes_within(&self, layer_name: &str, region: &Region) -> Layer {
297297- let number_of_objects = rand::thread_rng().gen_range(self.objects_count_range.clone());
298298- self.n_random_linelikes_within(layer_name, region, number_of_objects)
299299- }
300300-301301- pub fn random_object_within(&self, region: &Region) -> Object {
302302- let start = self.random_point(region);
303303- match rand::thread_rng().gen_range(1..=7) {
304304- 1 => self.random_polygon(region),
305305- 2 => Object::BigCircle(start),
306306- 3 => Object::SmallCircle(start),
307307- 4 => Object::Dot(start),
308308- 5 => Object::CurveInward(
309309- start,
310310- self.random_end_anchor(start, region),
311311- self.object_sizes.default_line_width,
312312- ),
313313- 6 => Object::CurveOutward(
314314- start,
315315- self.random_end_anchor(start, region),
316316- self.object_sizes.default_line_width,
317317- ),
318318- 7 => Object::Line(
319319- self.random_point(region),
320320- self.random_point(region),
321321- self.object_sizes.default_line_width,
322322- ),
323323- _ => unreachable!(),
324324- }
325325- }
326326-327327- pub fn random_linelike_within(&self, region: &Region) -> Object {
328328- let start = self.random_point(region);
329329- match rand::thread_rng().gen_range(1..=3) {
330330- 1 => Object::CurveInward(
331331- start,
332332- self.random_end_anchor(start, region),
333333- self.object_sizes.default_line_width,
334334- ),
335335- 2 => Object::CurveOutward(
336336- start,
337337- self.random_end_anchor(start, region),
338338- self.object_sizes.default_line_width,
339339- ),
340340- 3 => Object::Line(
341341- self.random_point(region),
342342- self.random_point(region),
343343- self.object_sizes.default_line_width,
344344- ),
345345- _ => unreachable!(),
346346- }
347347- }
348348-349349- pub fn random_end_anchor(&self, start: Point, region: &Region) -> Point {
350350- // End anchors are always a square diagonal from the start anchor (for now)
351351- // that means taking steps of the form n * (one of (1, 1), (1, -1), (-1, 1), (-1, -1))
352352- // Except that the end anchor needs to stay in the bounds of the shape.
353353-354354- // Determine all possible end anchors that are in a square diagonal from the start anchor
355355- let mut possible_end_anchors = vec![];
356356-357357- // shapes can end on the next cell, since that's where they end
358358- let actual_region = region.enlarged(1, 1);
359359-360360- for x in actual_region.mirrored_width_range() {
361361- for y in actual_region.mirrored_height_range() {
362362- let end_anchor = start.translated(x, y);
363363-364364- if end_anchor == start {
365365- continue;
366366- }
367367-368368- // Check that the end anchor is in a square diagonal from the start anchor and that the end anchor is in bounds
369369- if x.abs() == y.abs() && actual_region.contains(&end_anchor) {
370370- possible_end_anchors.push(end_anchor);
371371- }
372372- }
373373- }
374374-375375- // Pick a random end anchor from the possible end anchors
376376- possible_end_anchors[rand::thread_rng().gen_range(0..possible_end_anchors.len())]
377377- }
378378-379379- pub fn random_polygon(&self, region: &Region) -> Object {
380380- let number_of_anchors = rand::thread_rng().gen_range(self.polygon_vertices_range.clone());
381381- let start = self.random_point(region);
382382- let mut lines: Vec<LineSegment> = vec![];
383383- for _ in 0..number_of_anchors {
384384- let next_anchor = self.random_point(region);
385385- lines.push(self.random_line(next_anchor));
386386- }
387387- Object::Polygon(start, lines)
388388- }
389389-390390- pub fn random_line(&self, end: Point) -> LineSegment {
391391- match rand::thread_rng().gen_range(1..=3) {
392392- 1 => LineSegment::Straight(end),
393393- 2 => LineSegment::InwardCurve(end),
394394- 3 => LineSegment::OutwardCurve(end),
395395- _ => unreachable!(),
396396- }
397397- }
398398-399229 pub fn region_is_whole_grid(&self, region: &Region) -> bool {
400230 region.start == (0, 0) && region.end == self.grid_size
401231 }
402402-403403- pub fn random_region(&self) -> Region {
404404- let start = self.random_point(&self.world_region);
405405- let end = self.random_end_anchor(start, &self.world_region);
406406- Region::from(if start.0 > end.0 {
407407- (end, start)
408408- } else {
409409- (start, end)
410410- })
411411- }
412412-413413- pub fn random_point(&self, region: &Region) -> Point {
414414- region.ensure_nonempty().unwrap();
415415- Point(
416416- rand::thread_rng().gen_range(region.x_range()),
417417- rand::thread_rng().gen_range(region.y_range()),
418418- )
419419- }
420420-421232 pub fn clear(&mut self) {
422233 self.layers.clear();
423234 self.remove_background()
+1
src/lib.rs
···99pub mod rendering;
1010pub mod synchronization;
1111pub mod video;
1212+pub mod wasm;
12131314pub use geometry::{Angle, Containable, Point, Region};
1415pub use graphics::{
···44pub mod layer;
55pub mod objects;
66pub mod region;
77+pub mod point;
7889pub use canvas::*;
910pub use color::*;
···1112pub use layer::*;
1213pub use objects::*;
1314pub use region::*;
1515+pub use point::*;
···11+use crate::{Containable, Point, Region};
22+use rand::Rng;
33+44+impl Region {
55+ pub fn random_end(&self, start: Point) -> Point {
66+ // End anchors are always a square diagonal from the start anchor (for now)
77+ // that means taking steps of the form n * (one of (1, 1), (1, -1), (-1, 1), (-1, -1))
88+ // Except that the end anchor needs to stay in the bounds of the shape.
99+1010+ // Determine all possible end anchors that are in a square diagonal from the start anchor
1111+ let mut possible_end_anchors = vec![];
1212+1313+ // shapes can end on the next cell, since that's where they end
1414+ let actual_region = self.enlarged(1, 1);
1515+1616+ for x in actual_region.mirrored_width_range() {
1717+ for y in actual_region.mirrored_height_range() {
1818+ let end_anchor = start.translated(x, y);
1919+2020+ if end_anchor == start {
2121+ continue;
2222+ }
2323+2424+ // Check that the end anchor is in a square diagonal from the start anchor and that the end anchor is in bounds
2525+ if x.abs() == y.abs() && actual_region.contains(&end_anchor) {
2626+ possible_end_anchors.push(end_anchor);
2727+ }
2828+ }
2929+ }
3030+3131+ // Pick a random end anchor from the possible end anchors
3232+ possible_end_anchors[rand::thread_rng().gen_range(0..possible_end_anchors.len())]
3333+ }
3434+3535+ pub fn random(within: &Region) -> Self {
3636+ let start = Point::random(within);
3737+ let end = within.random_end(start);
3838+ Region::from(if start.0 > end.0 {
3939+ (end, start)
4040+ } else {
4141+ (start, end)
4242+ })
4343+ }
4444+4545+ pub fn random_point(&self) -> Point {
4646+ Point::random(self)
4747+ }
4848+4949+ pub fn random_point_except(&self, except: &Region) -> Point {
5050+ // XXX this is probably not a good idea lmao
5151+ loop {
5252+ let point = self.random_point();
5353+ if !except.contains(&point) {
5454+ return point;
5555+ }
5656+ }
5757+ }
5858+}