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