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