veloren_world/site/
generation.rs

1#[cfg(feature = "use-dyn-lib")]
2use {crate::LIB, std::ffi::CStr};
3
4use super::*;
5use crate::{
6    CanvasInfo, ColumnSample,
7    block::block_from_structure,
8    column::ColInfo,
9    util::{RandomField, Sampler},
10};
11use common::{
12    generation::EntityInfo,
13    store::{Id, Store},
14    terrain::{
15        Block, BlockKind, SpriteCfg,
16        structure::{Structure as PrefabStructure, StructureBlock},
17    },
18    util::Dir2,
19    vol::ReadVol,
20};
21use num::cast::AsPrimitive;
22use std::{
23    cell::RefCell,
24    f32::consts::{PI, TAU},
25    ops::RangeBounds,
26    sync::Arc,
27};
28use vek::*;
29
30#[derive(strum::Display)]
31pub enum Primitive {
32    Empty, // Placeholder
33
34    // Shapes
35    Aabb(Aabb<i32>),
36    Pyramid {
37        aabb: Aabb<i32>,
38        inset: i32,
39    },
40    Ramp {
41        aabb: Aabb<i32>,
42        inset: i32,
43        dir: Dir2,
44    },
45    Gable {
46        aabb: Aabb<i32>,
47        inset: i32,
48        // X axis parallel or Y axis parallel
49        dir: Dir2,
50    },
51    Cylinder(Aabb<i32>),
52    Cone(Aabb<i32>),
53    Sphere(Aabb<i32>),
54    /// An Aabb with rounded corners. The degree relates to how rounded the
55    /// corners are. A value less than 1.0 results in concave faces. A value
56    /// of 2.0 results in an ellipsoid. Values greater than 2.0 result in a
57    /// rounded aabb. Values less than 0.0 are clamped to 0.0 as negative values
58    /// would theoretically yield shapes extending to infinity.
59    Superquadric {
60        aabb: Aabb<i32>,
61        degree: f32,
62    },
63    Plane(Aabr<i32>, Vec3<i32>, Vec2<f32>),
64    /// A line segment from start to finish point with a given radius for both
65    /// points
66    Segment {
67        segment: LineSegment3<f32>,
68        r0: f32,
69        r1: f32,
70    },
71    /// A prism created by projecting a line segment with a given radius along
72    /// the z axis up to a provided height
73    SegmentPrism {
74        segment: LineSegment3<f32>,
75        radius: f32,
76        height: f32,
77    },
78    /// A sampling function is always a subset of another primitive to avoid
79    /// needing infinite bounds
80    Sampling(Id<Primitive>, Box<dyn Fn(Vec3<i32>) -> bool>),
81    ColSampling(Id<Primitive>, Box<dyn Fn(Vec3<i32>, &ColInfo) -> bool>),
82    Prefab(Box<PrefabStructure>),
83
84    // Combinators
85    Intersect(Id<Primitive>, Id<Primitive>),
86    Union(Id<Primitive>, Id<Primitive>),
87    // Not commutative
88    Without(Id<Primitive>, Id<Primitive>),
89    // Operators
90    Translate(Id<Primitive>, Vec3<i32>),
91    Scale(Id<Primitive>, Vec3<f32>),
92    RotateAbout(Id<Primitive>, Mat3<i32>, Vec3<f32>),
93    /// Repeat a primitive a number of times in a given direction, overlapping
94    /// between repeats are unspecified.
95    Repeat(Id<Primitive>, Vec3<i32>, u32),
96}
97
98impl std::fmt::Debug for Primitive {
99    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
100        match self {
101            Primitive::Empty => f.debug_tuple("Empty").finish(),
102            Primitive::Aabb(aabb) => f.debug_tuple("Aabb").field(&aabb).finish(),
103            Primitive::Pyramid { aabb, inset } => {
104                f.debug_tuple("Pyramid").field(&aabb).field(&inset).finish()
105            },
106            Primitive::Ramp { aabb, inset, dir } => f
107                .debug_tuple("Ramp")
108                .field(&aabb)
109                .field(&inset)
110                .field(&dir)
111                .finish(),
112            Primitive::Gable { aabb, inset, dir } => f
113                .debug_tuple("Gable")
114                .field(&aabb)
115                .field(&inset)
116                .field(&dir)
117                .finish(),
118            Primitive::Cylinder(aabb) => f.debug_tuple("Cylinder").field(&aabb).finish(),
119            Primitive::Cone(aabb) => f.debug_tuple("Cone").field(&aabb).finish(),
120            Primitive::Sphere(aabb) => f.debug_tuple("Sphere").field(&aabb).finish(),
121            Primitive::Superquadric { aabb, degree } => f
122                .debug_tuple("Superquadric")
123                .field(&aabb)
124                .field(&degree)
125                .finish(),
126            Primitive::Plane(aabr, origin, gradient) => f
127                .debug_tuple("Plane")
128                .field(&aabr)
129                .field(&origin)
130                .field(&gradient)
131                .finish(),
132            Primitive::Segment { segment, r0, r1 } => f
133                .debug_tuple("Segment")
134                .field(&segment)
135                .field(&r0)
136                .field(&r1)
137                .finish(),
138            Primitive::SegmentPrism {
139                segment,
140                radius,
141                height,
142            } => f
143                .debug_tuple("SegmentPrism")
144                .field(&segment)
145                .field(&radius)
146                .field(&height)
147                .finish(),
148            Primitive::Sampling(prim, _) => f.debug_tuple("Sampling").field(&prim).finish(),
149            Primitive::ColSampling(prim, _) => f.debug_tuple("ColSampling").field(&prim).finish(),
150            Primitive::Prefab(prefab) => f.debug_tuple("Prefab").field(&prefab).finish(),
151            Primitive::Intersect(a, b) => f.debug_tuple("Intersect").field(&a).field(&b).finish(),
152            Primitive::Union(a, b) => f.debug_tuple("Union").field(&a).field(&b).finish(),
153            Primitive::Without(a, b) => f.debug_tuple("Without").field(&a).field(&b).finish(),
154            Primitive::Translate(a, vec) => {
155                f.debug_tuple("Translate").field(&a).field(&vec).finish()
156            },
157            Primitive::Scale(a, vec) => f.debug_tuple("Scale").field(&a).field(&vec).finish(),
158            Primitive::RotateAbout(a, mat, vec) => f
159                .debug_tuple("RotateAbout")
160                .field(&a)
161                .field(&mat)
162                .field(&vec)
163                .finish(),
164            Primitive::Repeat(a, offset, n) => f
165                .debug_tuple("Repeat")
166                .field(&a)
167                .field(&offset)
168                .field(&n)
169                .finish(),
170        }
171    }
172}
173
174impl Primitive {
175    pub fn intersect(a: impl Into<Id<Primitive>>, b: impl Into<Id<Primitive>>) -> Self {
176        Self::Intersect(a.into(), b.into())
177    }
178
179    pub fn union(a: impl Into<Id<Primitive>>, b: impl Into<Id<Primitive>>) -> Self {
180        Self::Union(a.into(), b.into())
181    }
182
183    pub fn without(a: impl Into<Id<Primitive>>, b: impl Into<Id<Primitive>>) -> Self {
184        Self::Without(a.into(), b.into())
185    }
186
187    pub fn sampling(a: impl Into<Id<Primitive>>, f: Box<dyn Fn(Vec3<i32>) -> bool>) -> Self {
188        Self::Sampling(a.into(), f)
189    }
190
191    pub fn column_sampling(
192        a: impl Into<Id<Primitive>>,
193        f: Box<dyn Fn(Vec3<i32>, &ColInfo) -> bool>,
194    ) -> Self {
195        Self::ColSampling(a.into(), f)
196    }
197
198    pub fn translate(a: impl Into<Id<Primitive>>, trans: Vec3<i32>) -> Self {
199        Self::Translate(a.into(), trans)
200    }
201
202    #[allow(dead_code)] // TODO: check if correct - part of toolchaon update
203    pub fn scale(a: impl Into<Id<Primitive>>, scale: Vec3<f32>) -> Self {
204        Self::Scale(a.into(), scale)
205    }
206
207    pub fn rotate_about(
208        a: impl Into<Id<Primitive>>,
209        rot: Mat3<i32>,
210        point: Vec3<impl AsPrimitive<f32>>,
211    ) -> Self {
212        Self::RotateAbout(a.into(), rot, point.as_())
213    }
214
215    /// Rotates a primitive 90 degrees CCW about the Z axis `n` times
216    pub fn rotate_z_90_about(
217        a: impl Into<Id<Primitive>>,
218        n: i32,
219        point: Vec3<impl AsPrimitive<f32>>,
220    ) -> Self {
221        let rot = match n % 4 {
222            1 => Mat3::new(0, -1, 0, 1, 0, 0, 0, 0, 1),
223            2 => Mat3::new(-1, 0, 0, 0, -1, 0, 0, 0, 1),
224            3 => Mat3::new(0, 1, 0, -1, 0, 0, 0, 0, 1),
225            _ => Mat3::new(1, 0, 0, 0, 1, 0, 0, 0, 1),
226        };
227        Self::RotateAbout(a.into(), rot, point.as_())
228    }
229
230    pub fn repeat(a: impl Into<Id<Primitive>>, offset: Vec3<i32>, count: u32) -> Self {
231        Self::Repeat(a.into(), offset, count)
232    }
233}
234
235#[derive(Clone)]
236pub enum Fill {
237    Sprite(Block),
238    ResourceSprite(Block),
239    CfgSprite(Block, SpriteCfg),
240
241    Block(Block),
242    Brick(BlockKind, Rgb<u8>, u8),
243    PlankWall(BlockKind, Rgb<u8>, u8),
244    Checker(BlockKind, Rgb<u8>, Rgb<u8>, Vec2<i32>),
245    Gradient(util::gradient::Gradient, BlockKind),
246    GradientBrick(util::gradient::Gradient, BlockKind, u8),
247    // TODO: the offset field for Prefab is a hack that breaks the compositionality of Translate,
248    // we probably need an evaluator for the primitive tree that gets which point is queried at
249    // leaf nodes given an input point to make Translate/Rotate work generally
250    Prefab(Box<PrefabStructure>, Vec3<i32>, u32),
251    Sampling(Arc<dyn Fn(Vec3<i32>) -> Option<Block>>),
252}
253
254impl Fill {
255    pub fn sprite(kind: SpriteKind) -> Self { Fill::Block(Block::empty().with_sprite(kind)) }
256
257    pub fn sprite_ori(kind: SpriteKind, ori: u8) -> Self {
258        let block = Block::empty().with_sprite(kind);
259
260        let block = block.with_ori(ori).unwrap_or(block);
261        Fill::Sprite(block)
262    }
263
264    pub fn resource_sprite(kind: SpriteKind) -> Self {
265        Fill::ResourceSprite(Block::empty().with_sprite(kind))
266    }
267
268    pub fn resource_sprite_ori(kind: SpriteKind, ori: u8) -> Self {
269        let block = Block::empty().with_sprite(kind);
270
271        let block = block.with_ori(ori).unwrap_or(block);
272        Fill::ResourceSprite(block)
273    }
274
275    pub fn owned_resource_sprite_ori(kind: SpriteKind, ori: u8) -> Self {
276        let block = Block::empty().with_sprite(kind);
277
278        let block = block.with_ori(ori).unwrap_or(block);
279        let block = block
280            .with_attr(common::terrain::sprite::Owned(true))
281            .unwrap_or(block);
282
283        Fill::ResourceSprite(block)
284    }
285
286    pub fn sprite_ori_cfg(kind: SpriteKind, ori: u8, cfg: SpriteCfg) -> Self {
287        let block = Block::empty().with_sprite(kind);
288
289        let block = block.with_ori(ori).unwrap_or(block);
290        Fill::CfgSprite(block, cfg)
291    }
292
293    fn contains_at(
294        tree: &Store<Primitive>,
295        prim: Id<Primitive>,
296        pos: Vec3<i32>,
297        col: &ColInfo,
298    ) -> bool {
299        // Custom closure because vek's impl of `contains_point` is inclusive :(
300        let aabb_contains = |aabb: Aabb<i32>, pos: Vec3<i32>| {
301            (aabb.min.x..aabb.max.x).contains(&pos.x)
302                && (aabb.min.y..aabb.max.y).contains(&pos.y)
303                && (aabb.min.z..aabb.max.z).contains(&pos.z)
304        };
305
306        match &tree[prim] {
307            Primitive::Empty => false,
308
309            Primitive::Aabb(aabb) => aabb_contains(*aabb, pos),
310            Primitive::Ramp { aabb, inset, dir } => {
311                let inset = (*inset).max(aabb.size().reduce_min());
312                let inner = match dir {
313                    Dir2::X => Aabr {
314                        min: Vec2::new(aabb.min.x - 1 + inset, aabb.min.y),
315                        max: Vec2::new(aabb.max.x, aabb.max.y),
316                    },
317                    Dir2::NegX => Aabr {
318                        min: Vec2::new(aabb.min.x, aabb.min.y),
319                        max: Vec2::new(aabb.max.x - inset, aabb.max.y),
320                    },
321                    Dir2::Y => Aabr {
322                        min: Vec2::new(aabb.min.x, aabb.min.y - 1 + inset),
323                        max: Vec2::new(aabb.max.x, aabb.max.y),
324                    },
325                    Dir2::NegY => Aabr {
326                        min: Vec2::new(aabb.min.x, aabb.min.y),
327                        max: Vec2::new(aabb.max.x, aabb.max.y - inset),
328                    },
329                };
330                aabb_contains(*aabb, pos)
331                    && inner.is_valid()
332                    && (inner.projected_point(pos.xy()) - pos.xy())
333                        .map(|e| e.abs())
334                        .reduce_max() as f32
335                        / (inset as f32)
336                        < 1.0
337                            - ((pos.z - aabb.min.z) as f32 + 0.5) / (aabb.max.z - aabb.min.z) as f32
338            },
339            Primitive::Pyramid { aabb, inset } => {
340                let inset = (*inset).max(aabb.size().reduce_min());
341                let inner = Aabr {
342                    min: aabb.min.xy() - 1 + inset,
343                    max: aabb.max.xy() - inset,
344                }
345                .made_valid();
346                aabb_contains(*aabb, pos)
347                    && inner.is_valid()
348                    && (inner.projected_point(pos.xy()) - pos.xy())
349                        .map(|e| e.abs())
350                        .reduce_max() as f32
351                        / (inset as f32)
352                        < 1.0
353                            - ((pos.z - aabb.min.z) as f32 + 0.5) / (aabb.max.z - aabb.min.z) as f32
354            },
355            Primitive::Gable { aabb, inset, dir } => {
356                let inset = (*inset).max(aabb.size().reduce_min());
357                let inner = if dir.is_y() {
358                    Aabr {
359                        min: Vec2::new(aabb.min.x - 1 + inset, aabb.min.y),
360                        max: Vec2::new(aabb.max.x - inset, aabb.max.y),
361                    }
362                    .made_valid()
363                } else {
364                    Aabr {
365                        min: Vec2::new(aabb.min.x, aabb.min.y - 1 + inset),
366                        max: Vec2::new(aabb.max.x, aabb.max.y - inset),
367                    }
368                    .made_valid()
369                };
370                aabb_contains(*aabb, pos)
371                    && inner.is_valid()
372                    && (inner.projected_point(pos.xy()) - pos.xy())
373                        .map(|e| e.abs())
374                        .reduce_max() as f32
375                        / (inset as f32)
376                        < 1.0
377                            - ((pos.z - aabb.min.z) as f32 + 0.5) / (aabb.max.z - aabb.min.z) as f32
378            },
379            Primitive::Cylinder(aabb) => {
380                // Add 0.5 since the aabb is exclusive.
381                let fpos = pos.as_::<f32>().xy() - aabb.as_::<f32>().center().xy() + 0.5;
382                let size = Vec3::from(aabb.size().as_::<f32>()).xy();
383                (aabb.min.z..aabb.max.z).contains(&pos.z)
384                    && (2.0 * fpos / size).magnitude_squared() <= 1.0
385            },
386            Primitive::Cone(aabb) => {
387                (aabb.min.z..aabb.max.z).contains(&pos.z)
388                    && pos
389                        .xy()
390                        .as_()
391                        .distance_squared(aabb.as_().center().xy() - 0.5)
392                        < (((aabb.max.z - pos.z) as f32 / aabb.size().d as f32)
393                            * (aabb.size().w.min(aabb.size().h) as f32 / 2.0))
394                            .powi(2)
395            },
396            Primitive::Sphere(aabb) => {
397                aabb_contains(*aabb, pos)
398                    && pos.as_().distance_squared(aabb.as_().center() - 0.5)
399                        < (aabb.size().w.min(aabb.size().h) as f32 / 2.0).powi(2)
400            },
401            Primitive::Superquadric { aabb, degree } => {
402                let degree = degree.max(0.0);
403                let center = aabb.center().map(|e| e as f32);
404                let a: f32 = aabb.max.x as f32 - center.x - 0.5;
405                let b: f32 = aabb.max.y as f32 - center.y - 0.5;
406                let c: f32 = aabb.max.z as f32 - center.z - 0.5;
407                let rpos = pos.as_::<f32>() + 0.5 - center;
408                aabb_contains(*aabb, pos)
409                    && (rpos.x / a).abs().powf(degree)
410                        + (rpos.y / b).abs().powf(degree)
411                        + (rpos.z / c).abs().powf(degree)
412                        < 1.0
413            },
414            Primitive::Plane(aabr, origin, gradient) => {
415                // Maybe <= instead of ==
416                (aabr.min.x..aabr.max.x).contains(&pos.x)
417                    && (aabr.min.y..aabr.max.y).contains(&pos.y)
418                    && pos.z
419                        == origin.z
420                            + ((pos.xy() - origin.xy())
421                                .map(|x| x.abs())
422                                .as_()
423                                .dot(*gradient) as i32)
424            },
425            // TODO: Aabb calculation could be improved here by only considering the relevant radius
426            Primitive::Segment { segment, r0, r1 } => {
427                let distance = segment.end - segment.start;
428                let length = pos - segment.start.as_();
429                let t =
430                    (length.as_().dot(distance) / distance.magnitude_squared()).clamped(0.0, 1.0);
431                segment.distance_to_point(pos.map(|e| e as f32)) < Lerp::lerp(r0, r1, t) - 0.25
432            },
433            Primitive::SegmentPrism {
434                segment,
435                radius,
436                height,
437            } => {
438                let segment_2d = LineSegment2 {
439                    start: segment.start.xy(),
440                    end: segment.end.xy(),
441                };
442                let projected_point_2d: Vec2<f32> =
443                    segment_2d.as_().projected_point(pos.xy().as_());
444                let xy_check = projected_point_2d.distance(pos.xy().as_()) < radius - 0.25;
445                let projected_z = {
446                    let len_sq: f32 = segment_2d
447                        .start
448                        .as_()
449                        .distance_squared(segment_2d.end.as_());
450                    if len_sq < 0.1 {
451                        segment.start.z
452                    } else {
453                        let frac = ((projected_point_2d - segment_2d.start.as_())
454                            .dot(segment_2d.end.as_() - segment_2d.start.as_())
455                            / len_sq)
456                            .clamp(0.0, 1.0);
457                        (segment.end.z - segment.start.z) * frac + segment.start.z
458                    }
459                };
460                let z_check = (projected_z..=(projected_z + height)).contains(&(pos.z as f32));
461                xy_check && z_check
462            },
463            Primitive::Sampling(a, f) => Self::contains_at(tree, *a, pos, col) && f(pos),
464            Primitive::ColSampling(a, f) => Self::contains_at(tree, *a, pos, col) && f(pos, col),
465            Primitive::Prefab(p) => !matches!(p.get(pos), Err(_) | Ok(StructureBlock::None)),
466            Primitive::Intersect(a, b) => {
467                Self::contains_at(tree, *a, pos, col) && Self::contains_at(tree, *b, pos, col)
468            },
469            Primitive::Union(a, b) => {
470                Self::contains_at(tree, *a, pos, col) || Self::contains_at(tree, *b, pos, col)
471            },
472            Primitive::Without(a, b) => {
473                Self::contains_at(tree, *a, pos, col) && !Self::contains_at(tree, *b, pos, col)
474            },
475            Primitive::Translate(prim, vec) => {
476                Self::contains_at(tree, *prim, pos.map2(*vec, i32::saturating_sub), col)
477            },
478            Primitive::Scale(prim, vec) => {
479                let center = Self::get_bounds(tree, *prim).as_::<f32>().center();
480                let fpos = pos.as_::<f32>();
481                let spos = (center + ((fpos - center) / vec))
482                    .map(|x| x.round())
483                    .as_::<i32>();
484                Self::contains_at(tree, *prim, spos, col)
485            },
486            Primitive::RotateAbout(prim, mat, vec) => {
487                let mat = mat.as_::<f32>().transposed();
488                let vec = vec - 0.5;
489                Self::contains_at(
490                    tree,
491                    *prim,
492                    (vec + mat * (pos.as_::<f32>() - vec)).as_(),
493                    col,
494                )
495            },
496            Primitive::Repeat(prim, offset, count) => {
497                if count == &0 {
498                    false
499                } else {
500                    let count = count - 1;
501                    let aabb = Self::get_bounds(tree, *prim);
502                    let aabb_corner = {
503                        let min_red = aabb.min.map2(*offset, |a, b| if b < 0 { 0 } else { a });
504                        let max_red = aabb.max.map2(*offset, |a, b| if b < 0 { a } else { 0 });
505                        min_red + max_red
506                    };
507                    let diff = pos - aabb_corner;
508                    let min = diff
509                        .map2(*offset, |a, b| if b == 0 { i32::MAX } else { a / b })
510                        .reduce_min()
511                        .clamp(0, count as i32);
512                    let pos = pos - offset * min;
513                    Self::contains_at(tree, *prim, pos, col)
514                }
515            },
516        }
517    }
518
519    /// Returns the expected block at the given position. For prefabricated
520    /// structures, the `StructureBlock` is returned, and if there is an
521    /// `EntitySpawner`, the entity path to load will be the returned as the
522    /// third String value.
523    pub fn sample_at(
524        &self,
525        tree: &Store<Primitive>,
526        prim: Id<Primitive>,
527        pos: Vec3<i32>,
528        canvas_info: &CanvasInfo,
529        old_block: Block,
530        sprite_cfg: &mut Option<SpriteCfg>,
531        col: &ColInfo,
532    ) -> (Option<Block>, Option<StructureBlock>, Option<String>) {
533        if Self::contains_at(tree, prim, pos, col) {
534            match self {
535                Fill::Sprite(sprite) | Fill::ResourceSprite(sprite) => (
536                    Some(if old_block.is_filled() {
537                        *sprite
538                    } else {
539                        old_block.with_data_of(*sprite)
540                    }),
541                    None,
542                    None,
543                ),
544                Fill::CfgSprite(sprite, cfg) => {
545                    *sprite_cfg = Some(cfg.clone());
546                    (
547                        Some(if old_block.is_filled() {
548                            *sprite
549                        } else {
550                            old_block.with_data_of(*sprite)
551                        }),
552                        None,
553                        None,
554                    )
555                },
556                Fill::Block(block) => (Some(*block), None, None),
557                Fill::Brick(bk, col, range) => {
558                    let pos = (pos + Vec3::new(pos.z, pos.z, 0)) / Vec3::new(2, 2, 1);
559                    (
560                        Some(Block::new(
561                            *bk,
562                            *col + ((((pos.x ^ pos.y ^ pos.z) as u8).reverse_bits() as u16
563                                * *range as u16)
564                                >> 8) as u8,
565                            // *col + (RandomField::new(13)
566                            //     .get(pos)
567                            //     % *range as u32) as u8,
568                        )),
569                        None,
570                        None,
571                    )
572                },
573                Fill::Checker(bk, col, col2, v) => (
574                    Some(Block::new(
575                        *bk,
576                        if (pos.xy() / *v).sum() & 1 == 0 {
577                            *col
578                        } else {
579                            *col2
580                        },
581                    )),
582                    None,
583                    None,
584                ),
585                Fill::PlankWall(bk, col, range) => (
586                    Some(Block::new(
587                        *bk,
588                        *col + (RandomField::new(13)
589                            .get((pos + Vec3::new(pos.z, pos.z, 0) * 8) / Vec3::new(16, 16, 1))
590                            % *range as u32) as u8,
591                    )),
592                    None,
593                    None,
594                ),
595                Fill::Gradient(gradient, bk) => (
596                    Some(Block::new(*bk, gradient.sample(pos.as_()))),
597                    None,
598                    None,
599                ),
600                Fill::GradientBrick(gradient, bk, range) => {
601                    let col = gradient.sample(pos.as_());
602                    let pos = (pos + Vec3::new(pos.z, pos.z, 0)) / Vec3::new(2, 2, 1);
603                    (
604                        Some(Block::new(
605                            *bk,
606                            col + ((((pos.x ^ pos.y ^ pos.z) as u8).reverse_bits() as u16
607                                * *range as u16)
608                                >> 8) as u8,
609                        )),
610                        None,
611                        None,
612                    )
613                },
614                Fill::Prefab(p, tr, seed) => {
615                    let sb_result = p.get(pos - tr);
616                    if sb_result.is_err() {
617                        return (None, None, None);
618                    }
619
620                    let sb = sb_result.unwrap();
621
622                    let col_sample = canvas_info.col(canvas_info.wpos);
623                    if col_sample.is_none() {
624                        return (None, None, None);
625                    }
626
627                    match block_from_structure(
628                        canvas_info.index,
629                        sb,
630                        pos - tr,
631                        p.get_bounds().center().xy(),
632                        *seed,
633                        col_sample.unwrap(),
634                        || Block::air(SpriteKind::Empty),
635                        canvas_info.calendar(),
636                        &Vec2::new(Vec2::new(1, 0), Vec2::new(0, 1)),
637                    ) {
638                        Some((block, cfg, entity_path)) => {
639                            *sprite_cfg = cfg;
640                            if let Some(entity_path_str) = entity_path {
641                                (
642                                    Some(block),
643                                    Some(sb.clone()),
644                                    Some(entity_path_str.to_string()),
645                                )
646                            } else {
647                                (Some(block), Some(sb.clone()), None)
648                            }
649                        },
650                        _ => (None, None, None),
651                    }
652                },
653                Fill::Sampling(f) => (f(pos), None, None),
654            }
655        } else {
656            (None, None, None)
657        }
658    }
659
660    fn get_bounds_inner(tree: &Store<Primitive>, prim: Id<Primitive>) -> Vec<Aabb<i32>> {
661        fn or_zip_with<T, F: FnOnce(T, T) -> T>(a: Option<T>, b: Option<T>, f: F) -> Option<T> {
662            match (a, b) {
663                (Some(a), Some(b)) => Some(f(a, b)),
664                (Some(a), _) => Some(a),
665                (_, b) => b,
666            }
667        }
668
669        match &tree[prim] {
670            Primitive::Empty => vec![],
671            Primitive::Aabb(aabb) => vec![*aabb],
672            Primitive::Pyramid { aabb, .. } => vec![*aabb],
673            Primitive::Gable { aabb, .. } => vec![*aabb],
674            Primitive::Ramp { aabb, .. } => vec![*aabb],
675            Primitive::Cylinder(aabb) => vec![*aabb],
676            Primitive::Cone(aabb) => vec![*aabb],
677            Primitive::Sphere(aabb) => vec![*aabb],
678            Primitive::Superquadric { aabb, .. } => vec![*aabb],
679            Primitive::Plane(aabr, origin, gradient) => {
680                let half_size = aabr.half_size().reduce_max();
681                let longest_dist = ((aabr.center() - origin.xy()).map(|x| x.abs())
682                    + half_size
683                    + aabr.size().reduce_max() % 2)
684                    .map(|x| x as f32);
685                let z = if gradient.x.signum() == gradient.y.signum() {
686                    Vec2::new(0, longest_dist.dot(*gradient).round() as i32)
687                } else {
688                    (longest_dist * gradient).round().as_()
689                };
690                let aabb = Aabb {
691                    min: aabr.min.with_z(origin.z + z.reduce_min().min(0)),
692                    max: aabr.max.with_z(origin.z + z.reduce_max().max(0)),
693                };
694                vec![aabb.made_valid()]
695            },
696            Primitive::Segment { segment, r0, r1 } => {
697                let aabb = Aabb {
698                    min: segment.start,
699                    max: segment.end,
700                }
701                .made_valid();
702                vec![Aabb {
703                    min: (aabb.min - r0.max(*r1)).floor().as_(),
704                    max: (aabb.max + r0.max(*r1)).ceil().as_(),
705                }]
706            },
707            Primitive::SegmentPrism {
708                segment,
709                radius,
710                height,
711            } => {
712                let aabb = Aabb {
713                    min: segment.start,
714                    max: segment.end,
715                }
716                .made_valid();
717                let min = {
718                    let xy = (aabb.min.xy() - *radius).floor();
719                    xy.with_z(aabb.min.z).as_()
720                };
721                let max = {
722                    let xy = (aabb.max.xy() + *radius).ceil();
723                    xy.with_z((aabb.max.z + *height).ceil()).as_()
724                };
725                vec![Aabb { min, max }]
726            },
727            Primitive::Sampling(a, _) | Primitive::ColSampling(a, _) => {
728                Self::get_bounds_inner(tree, *a)
729            },
730            Primitive::Prefab(p) => vec![p.get_bounds()],
731            Primitive::Intersect(a, b) => or_zip_with(
732                Self::get_bounds_opt(tree, *a),
733                Self::get_bounds_opt(tree, *b),
734                |a, b| a.intersection(b),
735            )
736            .into_iter()
737            .collect(),
738
739            Primitive::Union(a, b) => {
740                fn jaccard(x: Aabb<i32>, y: Aabb<i32>) -> f32 {
741                    let s_intersection = x.intersection(y).size().as_::<f32>().magnitude();
742                    let s_union = x.union(y).size().as_::<f32>().magnitude();
743                    s_intersection / s_union
744                }
745                let mut inputs = Vec::new();
746                inputs.extend(Self::get_bounds_inner(tree, *a));
747                inputs.extend(Self::get_bounds_inner(tree, *b));
748                let mut results = Vec::new();
749                if let Some(aabb) = inputs.pop() {
750                    results.push(aabb);
751                    for a in &inputs {
752                        let best = results
753                            .iter()
754                            .enumerate()
755                            .max_by_key(|(_, b)| (jaccard(*a, **b) * 1000.0) as usize);
756                        match best {
757                            Some((i, b)) if jaccard(*a, *b) > 0.3 => {
758                                let mut aabb = results.swap_remove(i);
759                                aabb = aabb.union(*a);
760                                results.push(aabb);
761                            },
762                            _ => results.push(*a),
763                        }
764                    }
765                    results
766                } else {
767                    results
768                }
769            },
770            Primitive::Without(a, _) => Self::get_bounds_inner(tree, *a),
771            Primitive::Translate(prim, vec) => Self::get_bounds_inner(tree, *prim)
772                .into_iter()
773                .map(|aabb| Aabb {
774                    min: aabb.min.map2(*vec, i32::saturating_add),
775                    max: aabb.max.map2(*vec, i32::saturating_add),
776                })
777                .collect(),
778            Primitive::Scale(prim, vec) => Self::get_bounds_inner(tree, *prim)
779                .into_iter()
780                .map(|aabb| {
781                    let center = aabb.center();
782                    Aabb {
783                        min: center + ((aabb.min - center).as_::<f32>() * vec).as_::<i32>(),
784                        max: center + ((aabb.max - center).as_::<f32>() * vec).as_::<i32>(),
785                    }
786                })
787                .collect(),
788            Primitive::RotateAbout(prim, mat, vec) => Self::get_bounds_inner(tree, *prim)
789                .into_iter()
790                .map(|aabb| {
791                    let mat = mat.as_::<f32>();
792                    // - 0.5 because we want the point to be at the minimum of the voxel
793                    let vec = vec - 0.5;
794                    let new_aabb = Aabb::<f32> {
795                        min: vec + mat * (aabb.min.as_() - vec),
796                        // - 1 becuase we want the AABB to be inclusive when we rotate it, we then
797                        //   add 1 back to make it exclusive again
798                        max: vec + mat * ((aabb.max - 1).as_() - vec),
799                    }
800                    .made_valid();
801                    Aabb::<i32> {
802                        min: new_aabb.min.as_(),
803                        max: new_aabb.max.as_() + 1,
804                    }
805                })
806                .collect(),
807            Primitive::Repeat(prim, offset, count) => {
808                if count == &0 {
809                    vec![]
810                } else {
811                    let count = count - 1;
812                    Self::get_bounds_inner(tree, *prim)
813                        .into_iter()
814                        .map(|aabb| Aabb {
815                            min: aabb
816                                .min
817                                .map2(aabb.min + offset * count as i32, |a, b| a.min(b)),
818                            max: aabb
819                                .max
820                                .map2(aabb.max + offset * count as i32, |a, b| a.max(b)),
821                        })
822                        .collect()
823                }
824            },
825        }
826    }
827
828    pub fn get_bounds_disjoint(tree: &Store<Primitive>, prim: Id<Primitive>) -> Vec<Aabb<i32>> {
829        Self::get_bounds_inner(tree, prim)
830    }
831
832    pub fn get_bounds_opt(tree: &Store<Primitive>, prim: Id<Primitive>) -> Option<Aabb<i32>> {
833        Self::get_bounds_inner(tree, prim)
834            .into_iter()
835            .reduce(|a, b| a.union(b))
836    }
837
838    pub fn get_bounds(tree: &Store<Primitive>, prim: Id<Primitive>) -> Aabb<i32> {
839        Self::get_bounds_opt(tree, prim).unwrap_or_else(|| Aabb::new_empty(Vec3::zero()))
840    }
841}
842
843pub struct Painter {
844    prims: RefCell<Store<Primitive>>,
845    fills: RefCell<Vec<(Id<Primitive>, Fill)>>,
846    entities: RefCell<Vec<EntityInfo>>,
847    render_area: Aabr<i32>,
848}
849
850impl Painter {
851    /// Computes the depth of the tree rooted at `prim`
852    pub fn depth(&self, prim: Id<Primitive>) -> usize {
853        fn aux(prims: &Store<Primitive>, prim: Id<Primitive>, prev_depth: usize) -> usize {
854            match prims[prim] {
855                Primitive::Empty
856                | Primitive::Aabb(_)
857                | Primitive::Pyramid { .. }
858                | Primitive::Ramp { .. }
859                | Primitive::Gable { .. }
860                | Primitive::Cylinder(_)
861                | Primitive::Cone(_)
862                | Primitive::Sphere(_)
863                | Primitive::Superquadric { .. }
864                | Primitive::Plane(_, _, _)
865                | Primitive::Segment { .. }
866                | Primitive::SegmentPrism { .. }
867                | Primitive::Prefab(_) => prev_depth,
868                Primitive::Sampling(a, _)
869                | Primitive::ColSampling(a, _)
870                | Primitive::Translate(a, _)
871                | Primitive::Scale(a, _)
872                | Primitive::RotateAbout(a, _, _)
873                | Primitive::Repeat(a, _, _) => aux(prims, a, 1 + prev_depth),
874
875                Primitive::Intersect(a, b) | Primitive::Union(a, b) | Primitive::Without(a, b) => {
876                    aux(prims, a, 1 + prev_depth).max(aux(prims, b, 1 + prev_depth))
877                },
878            }
879        }
880        let prims = self.prims.borrow();
881        aux(&prims, prim, 0)
882    }
883
884    /// Orders two primitives by depth, since (A && (B && C)) is cheaper to
885    /// evaluate than ((A && B) && C) due to short-circuiting.
886    pub fn order_by_depth(
887        &self,
888        a: impl Into<Id<Primitive>>,
889        b: impl Into<Id<Primitive>>,
890    ) -> (Id<Primitive>, Id<Primitive>) {
891        let (a, b) = (a.into(), b.into());
892        if self.depth(a) < self.depth(b) {
893            (a, b)
894        } else {
895            (b, a)
896        }
897    }
898
899    /// Returns a `PrimitiveRef` of an axis aligned bounding box. The geometric
900    /// name of this shape is a "right rectangular prism."
901    pub fn aabb(&self, aabb: Aabb<i32>) -> PrimitiveRef<'_> {
902        self.prim(Primitive::Aabb(aabb.made_valid()))
903    }
904
905    /// Returns a `PrimitiveRef` of a sphere using a radius check.
906    pub fn sphere(&self, aabb: Aabb<i32>) -> PrimitiveRef<'_> {
907        self.prim(Primitive::Sphere(aabb.made_valid()))
908    }
909
910    /// Returns a `PrimitiveRef` of a sphere using a radius check where a radius
911    /// and origin are parameters instead of a bounding box.
912    pub fn sphere_with_radius(&self, origin: Vec3<i32>, radius: f32) -> PrimitiveRef<'_> {
913        let min = origin - Vec3::broadcast(radius.round() as i32);
914        let max = origin + Vec3::broadcast(radius.round() as i32);
915        self.prim(Primitive::Sphere(Aabb { min, max }))
916    }
917
918    /// Returns a `PrimitiveRef` of a sphere by returning an ellipsoid with
919    /// congruent legs. The voxel artifacts are slightly different from the
920    /// radius check `sphere()` method.
921    pub fn sphere2(&self, aabb: Aabb<i32>) -> PrimitiveRef<'_> {
922        let aabb = aabb.made_valid();
923        let radius = aabb.size().w.min(aabb.size().h) / 2;
924        let aabb = Aabb {
925            min: aabb.center() - radius,
926            max: aabb.center() + radius,
927        };
928        let degree = 2.0;
929        self.prim(Primitive::Superquadric { aabb, degree })
930    }
931
932    /// Returns a `PrimitiveRef` of an ellipsoid by constructing a superquadric
933    /// with a degree value of 2.0.
934    pub fn ellipsoid(&self, aabb: Aabb<i32>) -> PrimitiveRef<'_> {
935        let aabb = aabb.made_valid();
936        let degree = 2.0;
937        self.prim(Primitive::Superquadric { aabb, degree })
938    }
939
940    /// Returns a `PrimitiveRef` of a superquadric. A superquadric can be
941    /// thought of as a rounded Aabb where the degree determines how rounded
942    /// the corners are. Values from 0.0 to 1.0 produce concave faces or
943    /// "inverse rounded corners." A value of 1.0 produces a stretched
944    /// octahedron (or a non-stretched octahedron if the provided Aabb is a
945    /// cube). Values from 1.0 to 2.0 produce an octahedron with convex
946    /// faces. A degree of 2.0 produces an ellipsoid. Values larger than 2.0
947    /// produce a rounded Aabb. The degree cannot be less than 0.0 without
948    /// the shape extending to infinity.
949    pub fn superquadric(&self, aabb: Aabb<i32>, degree: f32) -> PrimitiveRef<'_> {
950        let aabb = aabb.made_valid();
951        self.prim(Primitive::Superquadric { aabb, degree })
952    }
953
954    /// Returns a `PrimitiveRef` of a rounded Aabb by producing a superquadric
955    /// with a degree value of 3.0.
956    pub fn rounded_aabb(&self, aabb: Aabb<i32>) -> PrimitiveRef<'_> {
957        let aabb = aabb.made_valid();
958        self.prim(Primitive::Superquadric { aabb, degree: 3.0 })
959    }
960
961    /// Returns a `PrimitiveRef` of the largest cylinder that fits in the
962    /// provided Aabb.
963    pub fn cylinder(&self, aabb: Aabb<i32>) -> PrimitiveRef<'_> {
964        self.prim(Primitive::Cylinder(aabb.made_valid()))
965    }
966
967    /// Returns a `PrimitiveRef` of the largest horizontal cylinder that fits in
968    /// the provided Aabb.
969    pub fn horizontal_cylinder(&self, aabb: Aabb<i32>, dir: Dir2) -> PrimitiveRef<'_> {
970        let aabr = Aabr::from(aabb);
971        let length = dir.select(aabr.size());
972        let height = aabb.max.z - aabb.min.z;
973        let aabb = Aabb {
974            min: (aabr.min - dir.abs().to_vec2() * height).with_z(aabb.min.z),
975            max: (dir.abs().select_with(aabr.min, aabr.max)).with_z(aabb.min.z + length),
976        };
977        self.cylinder(aabb)
978            .rotate_about((-dir.abs()).from_z_mat3(), aabr.min.with_z(aabb.min.z))
979    }
980
981    /// Returns a `PrimitiveRef` of a cylinder using a radius check where a
982    /// radius and origin are parameters instead of a bounding box.
983    pub fn cylinder_with_radius(
984        &self,
985        origin: Vec3<i32>,
986        radius: f32,
987        height: f32,
988    ) -> PrimitiveRef<'_> {
989        let min = origin - Vec2::broadcast(radius.round() as i32);
990        let max = origin + Vec2::broadcast(radius.round() as i32).with_z(height.round() as i32);
991        self.prim(Primitive::Cylinder(Aabb { min, max }))
992    }
993
994    /// Returns a `PrimitiveRef` of the largest cone that fits in the
995    /// provided Aabb.
996    pub fn cone(&self, aabb: Aabb<i32>) -> PrimitiveRef<'_> {
997        self.prim(Primitive::Cone(aabb.made_valid()))
998    }
999
1000    /// Returns a `PrimitiveRef` of a cone using a radius check where a radius
1001    /// and origin are parameters instead of a bounding box.
1002    pub fn cone_with_radius(
1003        &self,
1004        origin: Vec3<i32>,
1005        radius: f32,
1006        height: f32,
1007    ) -> PrimitiveRef<'_> {
1008        let min = origin - Vec2::broadcast(radius.round() as i32);
1009        let max = origin + Vec2::broadcast(radius.round() as i32).with_z(height.round() as i32);
1010        self.prim(Primitive::Cone(Aabb { min, max }))
1011    }
1012
1013    /// Returns a `PrimitiveRef` of a 3-dimensional line segment with a provided
1014    /// radius.
1015    pub fn line(
1016        &self,
1017        a: Vec3<impl AsPrimitive<f32>>,
1018        b: Vec3<impl AsPrimitive<f32>>,
1019        radius: f32,
1020    ) -> PrimitiveRef<'_> {
1021        self.prim(Primitive::Segment {
1022            segment: LineSegment3 {
1023                start: a.as_(),
1024                end: b.as_(),
1025            },
1026            r0: radius,
1027            r1: radius,
1028        })
1029    }
1030
1031    /// Returns a `PrimitiveRef` of a 3-dimensional line segment with two
1032    /// radius.
1033    pub fn line_two_radius(
1034        &self,
1035        a: Vec3<impl AsPrimitive<f32>>,
1036        b: Vec3<impl AsPrimitive<f32>>,
1037        r0: f32,
1038        r1: f32,
1039    ) -> PrimitiveRef<'_> {
1040        self.prim(Primitive::Segment {
1041            segment: LineSegment3 {
1042                start: a.as_(),
1043                end: b.as_(),
1044            },
1045            r0,
1046            r1,
1047        })
1048    }
1049
1050    /// Returns a `PrimitiveRef` of a 3-dimensional line segment where the
1051    /// provided radius only affects the width of the shape. The height of
1052    /// the shape is determined by the `height` parameter. The height of the
1053    /// shape is extended upwards along the z axis from the line. The top and
1054    /// bottom of the shape are planar and parallel to each other and the line.
1055    pub fn segment_prism(
1056        &self,
1057        a: Vec3<impl AsPrimitive<f32>>,
1058        b: Vec3<impl AsPrimitive<f32>>,
1059        radius: f32,
1060        height: f32,
1061    ) -> PrimitiveRef<'_> {
1062        let segment = LineSegment3 {
1063            start: a.as_(),
1064            end: b.as_(),
1065        };
1066        self.prim(Primitive::SegmentPrism {
1067            segment,
1068            radius,
1069            height,
1070        })
1071    }
1072
1073    /// Returns a `PrimitiveRef` of a 3-dimensional cubic bezier curve by
1074    /// dividing the curve into line segments with one segment approximately
1075    /// every length of 5 blocks.
1076    pub fn cubic_bezier(
1077        &self,
1078        start: Vec3<impl AsPrimitive<f32>>,
1079        ctrl0: Vec3<impl AsPrimitive<f32>>,
1080        ctrl1: Vec3<impl AsPrimitive<f32>>,
1081        end: Vec3<impl AsPrimitive<f32>>,
1082        radius: f32,
1083    ) -> PrimitiveRef<'_> {
1084        let bezier = CubicBezier3 {
1085            start: start.as_(),
1086            ctrl0: ctrl0.as_(),
1087            ctrl1: ctrl1.as_(),
1088            end: end.as_(),
1089        };
1090        let length = bezier.length_by_discretization(10);
1091        let num_segments = (0.2 * length).ceil() as u16;
1092        self.cubic_bezier_with_num_segments(bezier, radius, num_segments)
1093    }
1094
1095    /// Returns a `PrimitiveRef` of a 3-dimensional cubic bezier curve by
1096    /// dividing the curve into `num_segments` line segments.
1097    pub fn cubic_bezier_with_num_segments(
1098        &self,
1099        bezier: CubicBezier3<f32>,
1100        radius: f32,
1101        num_segments: u16,
1102    ) -> PrimitiveRef<'_> {
1103        let mut bezier_prim = self.empty();
1104        let range: Vec<_> = (0..=num_segments).collect();
1105        range.windows(2).for_each(|w| {
1106            let segment_start = bezier.evaluate(w[0] as f32 / num_segments as f32);
1107            let segment_end = bezier.evaluate(w[1] as f32 / num_segments as f32);
1108            bezier_prim = bezier_prim.union(self.line(segment_start, segment_end, radius));
1109        });
1110        bezier_prim
1111    }
1112
1113    /// Returns a `PrimitiveRef` of a 3-dimensional cubic bezier curve where the
1114    /// radius only governs the width of the curve. The height is governed
1115    /// by the `height` parameter where the shape extends upwards from the
1116    /// bezier curve by the value of `height`. The shape is constructed by
1117    /// dividing the curve into line segment prisms with one segment prism
1118    /// approximately every length of 5 blocks.
1119    pub fn cubic_bezier_prism(
1120        &self,
1121        start: Vec3<impl AsPrimitive<f32>>,
1122        ctrl0: Vec3<impl AsPrimitive<f32>>,
1123        ctrl1: Vec3<impl AsPrimitive<f32>>,
1124        end: Vec3<impl AsPrimitive<f32>>,
1125        radius: f32,
1126        height: f32,
1127    ) -> PrimitiveRef<'_> {
1128        let bezier = CubicBezier3 {
1129            start: start.as_(),
1130            ctrl0: ctrl0.as_(),
1131            ctrl1: ctrl1.as_(),
1132            end: end.as_(),
1133        };
1134        let length = bezier.length_by_discretization(10);
1135        let num_segments = (0.2 * length).ceil() as u16;
1136        self.cubic_bezier_prism_with_num_segments(bezier, radius, height, num_segments)
1137    }
1138
1139    /// Returns a `PrimitiveRef` of a 3-dimensional cubic bezier curve where the
1140    /// radius only governs the width of the curve. The height is governed
1141    /// by the `height` parameter where the shape extends upwards from the
1142    /// bezier curve by the value of `height`. The shape is constructed by
1143    /// dividing the curve into `num_segments` line segment prisms.
1144    pub fn cubic_bezier_prism_with_num_segments(
1145        &self,
1146        bezier: CubicBezier3<f32>,
1147        radius: f32,
1148        height: f32,
1149        num_segments: u16,
1150    ) -> PrimitiveRef<'_> {
1151        let mut bezier_prim = self.empty();
1152        let range: Vec<_> = (0..=num_segments).collect();
1153        range.windows(2).for_each(|w| {
1154            let segment_start = bezier.evaluate(w[0] as f32 / num_segments as f32);
1155            let segment_end = bezier.evaluate(w[1] as f32 / num_segments as f32);
1156            bezier_prim =
1157                bezier_prim.union(self.segment_prism(segment_start, segment_end, radius, height));
1158        });
1159        bezier_prim
1160    }
1161
1162    /// Returns a `PrimitiveRef` of a plane. The Aabr provides the bounds for
1163    /// the plane in the xy plane and the gradient determines its slope through
1164    /// the dot product. A gradient of <1.0, 0.0> creates a plane with a
1165    /// slope of 1.0 in the xz plane.
1166    pub fn plane(
1167        &self,
1168        aabr: Aabr<i32>,
1169        origin: Vec3<i32>,
1170        gradient: Vec2<f32>,
1171    ) -> PrimitiveRef<'_> {
1172        let aabr = aabr.made_valid();
1173        self.prim(Primitive::Plane(aabr, origin, gradient))
1174    }
1175
1176    /// Returns a `PrimitiveRef` of an Aabb with a slope cut into it. The
1177    /// `inset` governs the slope. The `dir` determines which direction the
1178    /// ramp points.
1179    pub fn ramp_inset(&self, aabb: Aabb<i32>, inset: i32, dir: Dir2) -> PrimitiveRef<'_> {
1180        let aabb = aabb.made_valid();
1181        self.prim(Primitive::Ramp { aabb, inset, dir })
1182    }
1183
1184    pub fn ramp(&self, aabb: Aabb<i32>, dir: Dir2) -> PrimitiveRef<'_> {
1185        let aabb = aabb.made_valid();
1186        self.prim(Primitive::Ramp {
1187            aabb,
1188            inset: dir.select((aabb.size().w, aabb.size().h)),
1189            dir,
1190        })
1191    }
1192
1193    /// Returns a `PrimitiveRef` of a triangular prism with the base being
1194    /// vertical. A gable is a tent shape. The `inset` governs the slope of
1195    /// the gable. The `dir` determines which way the gable points.
1196    pub fn gable(&self, aabb: Aabb<i32>, inset: i32, dir: Dir2) -> PrimitiveRef<'_> {
1197        let aabb = aabb.made_valid();
1198        self.prim(Primitive::Gable { aabb, inset, dir })
1199    }
1200
1201    /// Places a sprite at the provided location with the default rotation.
1202    pub fn sprite(&self, pos: Vec3<i32>, sprite: SpriteKind) {
1203        self.aabb(Aabb {
1204            min: pos,
1205            max: pos + 1,
1206        })
1207        .fill(Fill::sprite(sprite))
1208    }
1209
1210    /// Places a sprite at the provided location with the provided orientation.
1211    pub fn rotated_sprite(&self, pos: Vec3<i32>, sprite: SpriteKind, ori: u8) {
1212        self.aabb(Aabb {
1213            min: pos,
1214            max: pos + 1,
1215        })
1216        .fill(Fill::sprite_ori(sprite, ori % 8))
1217    }
1218
1219    /// Places a sprite at the provided location with the provided orientation
1220    /// and the provided [`SpriteCfg`].
1221    pub fn rotated_sprite_with_cfg(
1222        &self,
1223        pos: Vec3<i32>,
1224        sprite: SpriteKind,
1225        ori: u8,
1226        cfg: SpriteCfg,
1227    ) {
1228        self.aabb(Aabb {
1229            min: pos,
1230            max: pos + 1,
1231        })
1232        .fill(Fill::sprite_ori_cfg(sprite, ori % 8, cfg))
1233    }
1234
1235    /// Places a sprite at the provided location with the provided orientation
1236    /// which will be tracked by rtsim nature if the sprite has an associated
1237    /// [`veloren_common::rtsim::TerrainResource`].
1238    pub fn resource_sprite(&self, pos: Vec3<i32>, sprite: SpriteKind, ori: u8) {
1239        self.aabb(Aabb {
1240            min: pos,
1241            max: pos + 1,
1242        })
1243        .fill(Fill::resource_sprite_ori(sprite, ori % 8))
1244    }
1245
1246    /// Places a sprite at the provided location with the provided orientation
1247    /// which will be tracked by rtsim nature if the sprite has an associated
1248    /// [`veloren_common::rtsim::TerrainResource`].
1249    pub fn owned_resource_sprite(&self, pos: Vec3<i32>, sprite: SpriteKind, ori: u8) {
1250        self.aabb(Aabb {
1251            min: pos,
1252            max: pos + 1,
1253        })
1254        .fill(Fill::owned_resource_sprite_ori(sprite, ori))
1255    }
1256
1257    /// Returns a `PrimitiveRef` of the largest pyramid with a slope of 1 that
1258    /// fits in the provided Aabb.
1259    pub fn pyramid(&self, aabb: Aabb<i32>) -> PrimitiveRef<'_> {
1260        let inset = 0;
1261        let aabb = aabb.made_valid();
1262        self.prim(Primitive::Ramp {
1263            aabb,
1264            inset,
1265            dir: Dir2::X,
1266        })
1267        .intersect(self.prim(Primitive::Ramp {
1268            aabb,
1269            inset,
1270            dir: Dir2::NegX,
1271        }))
1272        .intersect(self.prim(Primitive::Ramp {
1273            aabb,
1274            inset,
1275            dir: Dir2::Y,
1276        }))
1277        .intersect(self.prim(Primitive::Ramp {
1278            aabb,
1279            inset,
1280            dir: Dir2::NegY,
1281        }))
1282    }
1283
1284    /// Used to create a new `PrimitiveRef`. Requires the desired `Primitive` to
1285    /// be supplied.
1286    pub fn prim(&self, prim: Primitive) -> PrimitiveRef<'_> {
1287        PrimitiveRef {
1288            id: self.prims.borrow_mut().insert(prim),
1289            painter: self,
1290        }
1291    }
1292
1293    /// Returns a `PrimitiveRef` of an empty primitive. Useful when additional
1294    /// primitives are unioned within a loop.
1295    pub fn empty(&self) -> PrimitiveRef<'_> { self.prim(Primitive::Empty) }
1296
1297    /// Fills the supplied primitive with the provided `Fill`.
1298    pub fn fill(&self, prim: impl Into<Id<Primitive>>, fill: Fill) {
1299        let prim = prim.into();
1300        if let Primitive::Union(a, b) = self.prims.borrow()[prim] {
1301            self.fill(a, fill.clone());
1302            self.fill(b, fill);
1303        } else {
1304            self.fills.borrow_mut().push((prim, fill));
1305        }
1306    }
1307
1308    /// ```text
1309    ///     ___
1310    ///    /  /\
1311    ///   /__/  |
1312    ///  /   \  |
1313    /// |     | /
1314    /// |_____|/
1315    /// ```
1316    /// A horizontal half cylinder on top of an `Aabb`.
1317    pub fn vault(&self, aabb: Aabb<i32>, dir: Dir2) -> PrimitiveRef<'_> {
1318        let h = dir.orthogonal().select(Vec3::from(aabb.size()).xy());
1319
1320        let mut prim = self.horizontal_cylinder(
1321            Aabb {
1322                min: aabb.min.with_z(aabb.max.z - h),
1323                max: aabb.max,
1324            },
1325            dir,
1326        );
1327
1328        if aabb.size().d < h {
1329            prim = prim.intersect(self.aabb(aabb));
1330        }
1331
1332        self.aabb(Aabb {
1333            min: aabb.min,
1334            max: aabb.max.with_z(aabb.max.z - h / 2),
1335        })
1336        .union(prim)
1337    }
1338
1339    /// Place aabbs around another aabb in a symmetric and distributed manner.
1340    pub fn aabbs_around_aabb(&self, aabb: Aabb<i32>, size: i32, offset: i32) -> PrimitiveRef<'_> {
1341        let pillar = self.aabb(Aabb {
1342            min: (aabb.min.xy() - 1).with_z(aabb.min.z),
1343            max: (aabb.min.xy() + size - 1).with_z(aabb.max.z),
1344        });
1345
1346        let true_offset = offset + size;
1347
1348        let size_x = aabb.max.x - aabb.min.x;
1349        let size_y = aabb.max.y - aabb.min.y;
1350
1351        let num_aabbs = ((size_x + 1) / 2) / true_offset;
1352        let x = pillar.repeat(Vec3::new(true_offset, 0, 0), num_aabbs as u32 + 1);
1353
1354        let num_aabbs = ((size_y + 1) / 2) / true_offset;
1355        let y = pillar.repeat(Vec3::new(0, true_offset, 0), num_aabbs as u32 + 1);
1356        let center = aabb.as_::<f32>().center();
1357        let shape = x.union(y);
1358        let shape =
1359            shape.union(shape.rotate_about(Mat3::new(-1, 0, 0, 0, -1, 0, 0, 0, -1), center));
1360        shape.union(shape.rotate_about(Mat3::new(0, 1, 0, -1, 0, 0, 0, 0, 1), center))
1361    }
1362
1363    pub fn staircase_in_aabb(
1364        &self,
1365        aabb: Aabb<i32>,
1366        thickness: i32,
1367        start_dir: Dir2,
1368    ) -> PrimitiveRef<'_> {
1369        let mut forward = start_dir;
1370        let mut z = aabb.max.z - 1;
1371        let aabr = Aabr::from(aabb);
1372
1373        let mut prim = self.empty();
1374
1375        while z > aabb.min.z {
1376            let right = forward.rotated_cw();
1377            let fc = forward.select_aabr(aabr);
1378            let corner =
1379                fc * forward.abs().to_vec2() + right.select_aabr(aabr) * right.abs().to_vec2();
1380            let aabb = Aabb {
1381                min: corner.with_z(z),
1382                max: (corner - (forward.to_vec2() + right.to_vec2()) * thickness).with_z(z + 1),
1383            }
1384            .made_valid();
1385
1386            let stair_len = ((fc - (-forward).select_aabr(aabr)).abs() - thickness * 2).max(1) + 1;
1387
1388            let stairs = self.ramp(
1389                Aabb {
1390                    min: (corner - right.to_vec2() * (stair_len + thickness)).with_z(z - stair_len),
1391                    max: (corner
1392                        - right.to_vec2() * (thickness - 1)
1393                        - forward.to_vec2() * thickness)
1394                        .with_z(z + 1),
1395                }
1396                .made_valid(),
1397                right,
1398            );
1399
1400            prim = prim
1401                .union(self.aabb(aabb))
1402                .union(stairs.without(stairs.translate(Vec3::new(0, 0, -2))));
1403
1404            z -= stair_len;
1405            forward = forward.rotated_ccw();
1406        }
1407        prim
1408    }
1409
1410    /// A simple numeral 0-9
1411    pub fn numeral(&self, origin: Vec3<i32>, numeral: usize) -> PrimitiveRef<'_> {
1412        let bottom_bar = self.aabb(Aabb {
1413            min: Vec2::new(origin.x, origin.y).with_z(origin.z),
1414            max: Vec2::new(origin.x + 1, origin.y + 4).with_z(origin.z + 1),
1415        });
1416        let mid_bar = self.aabb(Aabb {
1417            min: Vec2::new(origin.x, origin.y).with_z(origin.z + 2),
1418            max: Vec2::new(origin.x + 1, origin.y + 4).with_z(origin.z + 3),
1419        });
1420        let top_bar = self.aabb(Aabb {
1421            min: Vec2::new(origin.x, origin.y).with_z(origin.z + 4),
1422            max: Vec2::new(origin.x + 1, origin.y + 4).with_z(origin.z + 5),
1423        });
1424        let left_top = self.aabb(Aabb {
1425            min: Vec2::new(origin.x, origin.y).with_z(origin.z + 2),
1426            max: Vec2::new(origin.x + 1, origin.y + 1).with_z(origin.z + 5),
1427        });
1428        let left_bottom = self.aabb(Aabb {
1429            min: Vec2::new(origin.x, origin.y).with_z(origin.z),
1430            max: Vec2::new(origin.x + 1, origin.y + 1).with_z(origin.z + 3),
1431        });
1432        let right_top = self.aabb(Aabb {
1433            min: Vec2::new(origin.x, origin.y + 3).with_z(origin.z + 2),
1434            max: Vec2::new(origin.x + 1, origin.y + 4).with_z(origin.z + 5),
1435        });
1436        let right_bottom = self.aabb(Aabb {
1437            min: Vec2::new(origin.x, origin.y + 3).with_z(origin.z),
1438            max: Vec2::new(origin.x + 1, origin.y + 4).with_z(origin.z + 3),
1439        });
1440        let number_strokes = match numeral {
1441            0 => &[
1442                top_bar,
1443                bottom_bar,
1444                right_top,
1445                right_bottom,
1446                left_top,
1447                left_bottom,
1448            ] as &[_],
1449            1 => &[right_top, right_bottom],
1450            2 => &[top_bar, right_top, mid_bar, left_bottom, bottom_bar],
1451            3 => &[top_bar, bottom_bar, mid_bar, right_top, right_bottom],
1452            4 => &[left_top, mid_bar, right_top, right_bottom],
1453            5 => &[top_bar, left_top, mid_bar, right_bottom, bottom_bar],
1454            6 => &[
1455                top_bar,
1456                left_top,
1457                left_bottom,
1458                bottom_bar,
1459                right_bottom,
1460                mid_bar,
1461            ],
1462            7 => &[top_bar, right_top, right_bottom],
1463            8 => &[
1464                top_bar,
1465                left_top,
1466                left_bottom,
1467                mid_bar,
1468                right_top,
1469                right_bottom,
1470                bottom_bar,
1471            ],
1472            _ => &[top_bar, left_top, mid_bar, right_top, right_bottom],
1473        };
1474
1475        let mut prim = self.empty();
1476        for stroke in number_strokes {
1477            prim = prim.union(*stroke)
1478        }
1479
1480        prim
1481    }
1482
1483    pub fn column(&self, point: Vec2<i32>, range: impl RangeBounds<i32>) -> PrimitiveRef<'_> {
1484        self.aabb(Aabb {
1485            min: point.with_z(match range.start_bound() {
1486                std::ops::Bound::Included(n) => *n,
1487                std::ops::Bound::Excluded(n) => n + 1,
1488                std::ops::Bound::Unbounded => i32::MIN,
1489            }),
1490            max: (point + 1).with_z(match range.end_bound() {
1491                std::ops::Bound::Included(n) => n + 1,
1492                std::ops::Bound::Excluded(n) => *n,
1493                std::ops::Bound::Unbounded => i32::MAX,
1494            }),
1495        })
1496    }
1497
1498    /// The area that the canvas is currently rendering.
1499    pub fn render_aabr(&self) -> Aabr<i32> { self.render_area }
1500
1501    /// Spawns an entity if it is in the render_aabr, otherwise does nothing.
1502    pub fn spawn(&self, entity: EntityInfo) {
1503        if self.render_area.contains_point(entity.pos.xy().as_()) {
1504            self.entities.borrow_mut().push(entity)
1505        }
1506    }
1507}
1508
1509pub fn render_prefab(file_path: &str, position: Vec3<i32>, painter: &Painter) {
1510    let asset_handle = PrefabStructure::load_group(file_path);
1511    let prefab_structure = asset_handle.read()[0].clone();
1512
1513    // Render the prefab
1514    painter
1515        .prim(Primitive::Prefab(Box::new(prefab_structure.clone())))
1516        .translate(position)
1517        .fill(Fill::Prefab(Box::new(prefab_structure), position, 0));
1518}
1519
1520#[derive(Copy, Clone)]
1521pub struct PrimitiveRef<'a> {
1522    id: Id<Primitive>,
1523    painter: &'a Painter,
1524}
1525
1526impl<'a> From<PrimitiveRef<'a>> for Id<Primitive> {
1527    fn from(r: PrimitiveRef<'a>) -> Self { r.id }
1528}
1529
1530impl<'a> PrimitiveRef<'a> {
1531    /// Joins two primitives together by returning the total of the blocks of
1532    /// both primitives. In boolean logic this is an `OR` operation.
1533    #[must_use]
1534    pub fn union(self, other: impl Into<Id<Primitive>>) -> PrimitiveRef<'a> {
1535        let (a, b) = self.painter.order_by_depth(self, other);
1536        self.painter.prim(Primitive::union(a, b))
1537    }
1538
1539    /// Joins two primitives together by returning only overlapping blocks. In
1540    /// boolean logic this is an `AND` operation.
1541    #[must_use]
1542    pub fn intersect(self, other: impl Into<Id<Primitive>>) -> PrimitiveRef<'a> {
1543        let (a, b) = self.painter.order_by_depth(self, other);
1544        self.painter.prim(Primitive::intersect(a, b))
1545    }
1546
1547    /// Subtracts the blocks of the `other` primitive from `self`. In boolean
1548    /// logic this is a `NOT` operation.
1549    #[must_use]
1550    pub fn without(self, other: impl Into<Id<Primitive>>) -> PrimitiveRef<'a> {
1551        self.painter.prim(Primitive::without(self, other))
1552    }
1553
1554    /// Fills the primitive with `fill` and paints it into the world.
1555    pub fn fill(self, fill: Fill) { self.painter.fill(self, fill); }
1556
1557    /// Fills the primitive with empty blocks. This will subtract any
1558    /// blocks in the world that inhabit the same positions as the blocks in
1559    /// this primitive.
1560    pub fn clear(self) { self.painter.fill(self, Fill::Block(Block::empty())); }
1561
1562    /// Returns a `PrimitiveRef` that conforms to the provided sampling
1563    /// function.
1564    #[must_use]
1565    pub fn sample(self, sampling: impl Fn(Vec3<i32>) -> bool + 'static) -> PrimitiveRef<'a> {
1566        self.painter
1567            .prim(Primitive::sampling(self, Box::new(sampling)))
1568    }
1569
1570    /// Returns a `PrimitiveRef` that conforms to the provided sampling
1571    /// function.
1572    #[must_use]
1573    pub fn sample_with_column(
1574        self,
1575        sampling: impl Fn(Vec3<i32>, &ColInfo) -> bool + 'static,
1576    ) -> PrimitiveRef<'a> {
1577        self.painter
1578            .prim(Primitive::column_sampling(self, Box::new(sampling)))
1579    }
1580
1581    /// Rotates a primitive about it's own's bounds minimum point,
1582    #[must_use]
1583    pub fn rotate_about_min(self, mat: Mat3<i32>) -> PrimitiveRef<'a> {
1584        let point = Fill::get_bounds(&self.painter.prims.borrow(), self.into()).min;
1585        self.rotate_about(mat, point)
1586    }
1587}
1588
1589/// A trait to more easily manipulate groups of primitives.
1590pub trait PrimitiveTransform {
1591    /// Translates the primitive along the vector `trans`.
1592    #[must_use]
1593    fn translate(self, trans: Vec3<i32>) -> Self;
1594    /// Rotates the primitive about the given point by multiplying each block
1595    /// position by the provided rotation matrix.
1596    #[must_use]
1597    fn rotate_about(self, rot: Mat3<i32>, point: Vec3<impl AsPrimitive<f32>>) -> Self;
1598    /// Rotates the primitive 90 degrees counterclockwise about the given point
1599    /// along the Z axis n times.
1600    #[must_use]
1601    fn rotate_z_90_about(self, n: i32, point: Vec3<impl AsPrimitive<f32>>) -> Self;
1602    /// Scales the primitive along each axis by the x, y, and z components of
1603    /// the `scale` vector respectively.
1604    #[expect(dead_code)]
1605    #[must_use]
1606    fn scale(self, scale: Vec3<impl AsPrimitive<f32>>) -> Self;
1607    /// Returns a `PrimitiveRef` of the primitive in addition to the same
1608    /// primitive translated by `offset` and repeated `count` times, each time
1609    /// translated by an additional offset.
1610    #[must_use]
1611    fn repeat(self, offset: Vec3<i32>, count: u32) -> Self;
1612}
1613
1614impl PrimitiveTransform for PrimitiveRef<'_> {
1615    fn translate(self, trans: Vec3<i32>) -> Self {
1616        self.painter.prim(Primitive::translate(self, trans))
1617    }
1618
1619    fn rotate_about(self, rot: Mat3<i32>, point: Vec3<impl AsPrimitive<f32>>) -> Self {
1620        self.painter.prim(Primitive::rotate_about(self, rot, point))
1621    }
1622
1623    fn rotate_z_90_about(self, n: i32, point: Vec3<impl AsPrimitive<f32>>) -> Self {
1624        self.painter
1625            .prim(Primitive::rotate_z_90_about(self, n, point))
1626    }
1627
1628    fn scale(self, scale: Vec3<impl AsPrimitive<f32>>) -> Self {
1629        self.painter.prim(Primitive::scale(self, scale.as_()))
1630    }
1631
1632    fn repeat(self, offset: Vec3<i32>, count: u32) -> Self {
1633        self.painter.prim(Primitive::repeat(self, offset, count))
1634    }
1635}
1636
1637impl<const N: usize> PrimitiveTransform for [PrimitiveRef<'_>; N] {
1638    fn translate(mut self, trans: Vec3<i32>) -> Self {
1639        for prim in &mut self {
1640            *prim = prim.translate(trans);
1641        }
1642        self
1643    }
1644
1645    fn rotate_about(mut self, rot: Mat3<i32>, point: Vec3<impl AsPrimitive<f32>>) -> Self {
1646        for prim in &mut self {
1647            *prim = prim.rotate_about(rot, point);
1648        }
1649        self
1650    }
1651
1652    fn rotate_z_90_about(mut self, n: i32, point: Vec3<impl AsPrimitive<f32>>) -> Self {
1653        for prim in &mut self {
1654            *prim = prim.rotate_z_90_about(n, point);
1655        }
1656        self
1657    }
1658
1659    fn scale(mut self, scale: Vec3<impl AsPrimitive<f32>>) -> Self {
1660        for prim in &mut self {
1661            *prim = prim.scale(scale);
1662        }
1663        self
1664    }
1665
1666    fn repeat(mut self, offset: Vec3<i32>, count: u32) -> Self {
1667        for prim in &mut self {
1668            *prim = prim.repeat(offset, count);
1669        }
1670        self
1671    }
1672}
1673
1674pub trait Structure {
1675    #[cfg(feature = "use-dyn-lib")]
1676    const UPDATE_FN: &'static [u8];
1677
1678    fn render_inner(&self, _site: &Site, _land: &Land, _painter: &Painter) {}
1679
1680    fn render(&self, site: &Site, land: &Land, painter: &Painter) {
1681        #[cfg(not(feature = "use-dyn-lib"))]
1682        {
1683            self.render_inner(site, land, painter);
1684        }
1685        #[cfg(feature = "use-dyn-lib")]
1686        {
1687            let lock = LIB.lock().unwrap();
1688            let lib = &lock.as_ref().unwrap().lib;
1689
1690            let update_fn: common_dynlib::Symbol<fn(&Self, &Site, &Land, &Painter)> = unsafe {
1691                //let start = std::time::Instant::now();
1692                // Overhead of 0.5-5 us (could use hashmap to mitigate if this is an issue)
1693                lib.get(Self::UPDATE_FN)
1694                //println!("{}", start.elapsed().as_nanos());
1695            }
1696            .unwrap_or_else(|e| {
1697                panic!(
1698                    "Trying to use: {} but had error: {:?}",
1699                    CStr::from_bytes_with_nul(Self::UPDATE_FN)
1700                        .map(CStr::to_str)
1701                        .unwrap()
1702                        .unwrap(),
1703                    e
1704                )
1705            });
1706
1707            update_fn(self, site, land, painter);
1708        }
1709    }
1710
1711    // Generate a primitive tree and fills for this structure
1712    fn render_collect(
1713        &self,
1714        site: &Site,
1715        canvas: &CanvasInfo,
1716    ) -> (
1717        Store<Primitive>,
1718        Vec<(Id<Primitive>, Fill)>,
1719        Vec<EntityInfo>,
1720    ) {
1721        let painter = Painter {
1722            prims: RefCell::new(Store::default()),
1723            fills: RefCell::new(Vec::new()),
1724            entities: RefCell::new(Vec::new()),
1725            render_area: Aabr {
1726                min: canvas.wpos,
1727                max: canvas.wpos + TerrainChunkSize::RECT_SIZE.map(|e| e as i32),
1728            },
1729        };
1730
1731        self.render(site, &canvas.land(), &painter);
1732        (
1733            painter.prims.into_inner(),
1734            painter.fills.into_inner(),
1735            painter.entities.into_inner(),
1736        )
1737    }
1738
1739    /// What `z_off` in `terrain_surface_at` should be relative to for each
1740    /// column.
1741    fn rel_terrain_offset(&self, col: &ColumnSample) -> i32 { col.alt as i32 }
1742
1743    fn terrain_surface_at<R: Rng>(
1744        &self,
1745        _wpos: Vec2<i32>,
1746        _old: Block,
1747        _rng: &mut R,
1748        _col: &ColumnSample,
1749        _z_off: i32,
1750        _site: &Site,
1751    ) -> Option<Block> {
1752        None
1753    }
1754}
1755
1756/// Extend a 2d AABR to a 3d AABB
1757pub fn aabr_with_z<T>(aabr: Aabr<T>, z: Range<T>) -> Aabb<T> {
1758    Aabb {
1759        min: aabr.min.with_z(z.start),
1760        max: aabr.max.with_z(z.end),
1761    }
1762}
1763
1764#[expect(dead_code)]
1765/// Just the corners of an AABB, good for outlining stuff when debugging
1766pub fn aabb_corners<F: FnMut(Primitive) -> Id<Primitive>>(
1767    prim: &mut F,
1768    aabb: Aabb<i32>,
1769) -> Id<Primitive> {
1770    let f = |prim: &mut F, ret, vec| {
1771        let sub = prim(Primitive::Aabb(Aabb {
1772            min: aabb.min + vec,
1773            max: aabb.max - vec,
1774        }));
1775        prim(Primitive::Without(ret, sub))
1776    };
1777    let mut ret = prim(Primitive::Aabb(aabb));
1778    ret = f(prim, ret, Vec3::new(1, 0, 0));
1779    ret = f(prim, ret, Vec3::new(0, 1, 0));
1780    ret = f(prim, ret, Vec3::new(0, 0, 1));
1781    ret
1782}
1783
1784pub fn place_circular(
1785    center: Vec2<i32>,
1786    radius: f32,
1787    amount: i32,
1788) -> impl Iterator<Item = Vec2<i32>> {
1789    let phi = TAU / amount as f32;
1790    (1..=amount).map(move |n| {
1791        Vec2::new(
1792            center.x + (radius * ((n as f32 * phi).cos())) as i32,
1793            center.y + (radius * ((n as f32 * phi).sin())) as i32,
1794        )
1795    })
1796}
1797
1798pub fn place_circular_as_vec(center: Vec2<i32>, radius: f32, amount: i32) -> Vec<Vec2<i32>> {
1799    let phi = TAU / amount as f32;
1800    let mut positions = vec![];
1801    for n in 1..=amount {
1802        let pos = Vec2::new(
1803            center.x + (radius * ((n as f32 * phi).cos())) as i32,
1804            center.y + (radius * ((n as f32 * phi).sin())) as i32,
1805        );
1806        positions.push(pos);
1807    }
1808    positions
1809}
1810
1811pub fn spiral_staircase(
1812    origin: Vec3<i32>,
1813    radius: f32,
1814    inner_radius: f32,
1815    stretch: f32,
1816) -> Box<dyn Fn(Vec3<i32>) -> bool> {
1817    Box::new(move |pos: Vec3<i32>| {
1818        let pos = pos - origin;
1819        if (pos.xy().magnitude_squared() as f32) < inner_radius.powi(2) {
1820            true
1821        } else if (pos.xy().magnitude_squared() as f32) < radius.powi(2) {
1822            ((pos.x as f32).atan2(pos.y as f32) / (PI * 2.0) * stretch + pos.z as f32)
1823                .rem_euclid(stretch)
1824                < 1.5
1825        } else {
1826            false
1827        }
1828    })
1829}
1830
1831pub fn wall_staircase(
1832    origin: Vec3<i32>,
1833    radius: f32,
1834    stretch: f32,
1835) -> Box<dyn Fn(Vec3<i32>) -> bool> {
1836    Box::new(move |pos: Vec3<i32>| {
1837        let pos = pos - origin;
1838        if (pos.x.abs().max(pos.y.abs())) as f32 > 0.6 * radius {
1839            ((pos.x as f32).atan2(pos.y as f32) / (PI * 2.0) * stretch + pos.z as f32)
1840                .rem_euclid(stretch)
1841                < 1.0
1842        } else {
1843            false
1844        }
1845    })
1846}
1847
1848pub fn inscribed_polystar(
1849    origin: Vec2<i32>,
1850    radius: f32,
1851    sides: usize,
1852) -> Box<dyn Fn(Vec3<i32>) -> bool> {
1853    Box::new(move |pos| {
1854        use std::f32::consts::TAU;
1855        let rpos: Vec2<f32> = pos.xy().as_() - origin.as_();
1856        let is_border = rpos.magnitude_squared() > (radius - 2.0).powi(2);
1857        let is_line = (0..sides).any(|i| {
1858            let f = |j: f32| {
1859                let t = j * TAU / sides as f32;
1860                radius * Vec2::new(t.cos(), t.sin())
1861            };
1862            let line = LineSegment2 {
1863                start: f(i as f32),
1864                end: f((i + 2) as f32),
1865            };
1866            line.distance_to_point(rpos) <= 1.0
1867        });
1868        is_border || is_line
1869    })
1870}