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