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