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, 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 dir: Dir,
49 },
50 Cylinder(Aabb<i32>),
51 Cone(Aabb<i32>),
52 Sphere(Aabb<i32>),
53 Superquadric {
59 aabb: Aabb<i32>,
60 degree: f32,
61 },
62 Plane(Aabr<i32>, Vec3<i32>, Vec2<f32>),
63 Segment {
66 segment: LineSegment3<f32>,
67 r0: f32,
68 r1: f32,
69 },
70 SegmentPrism {
73 segment: LineSegment3<f32>,
74 radius: f32,
75 height: f32,
76 },
77 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 Intersect(Id<Primitive>, Id<Primitive>),
85 Union(Id<Primitive>, Id<Primitive>),
86 Without(Id<Primitive>, Id<Primitive>),
88 Translate(Id<Primitive>, Vec3<i32>),
90 Scale(Id<Primitive>, Vec3<f32>),
91 RotateAbout(Id<Primitive>, Mat3<i32>, Vec3<f32>),
92 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(°ree)
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)] 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 pub fn rotate_z_90_about(
216 a: impl Into<Id<Primitive>>,
217 n: i32,
218 point: Vec3<impl AsPrimitive<f32>>,
219 ) -> Self {
220 let rot = match n % 4 {
221 1 => Mat3::new(0, -1, 0, 1, 0, 0, 0, 0, 1),
222 2 => Mat3::new(-1, 0, 0, 0, -1, 0, 0, 0, 1),
223 3 => Mat3::new(0, 1, 0, -1, 0, 0, 0, 0, 1),
224 _ => Mat3::new(1, 0, 0, 0, 1, 0, 0, 0, 1),
225 };
226 Self::RotateAbout(a.into(), rot, point.as_())
227 }
228
229 pub fn repeat(a: impl Into<Id<Primitive>>, offset: Vec3<i32>, count: u32) -> Self {
230 Self::Repeat(a.into(), offset, count)
231 }
232}
233
234#[derive(Clone)]
235pub enum Fill {
236 Sprite(Block),
237 ResourceSprite(Block),
238 CfgSprite(Block, SpriteCfg),
239
240 Block(Block),
241 Brick(BlockKind, Rgb<u8>, u8),
242 PlankWall(BlockKind, Rgb<u8>, u8),
243 Gradient(util::gradient::Gradient, BlockKind),
244 GradientBrick(util::gradient::Gradient, BlockKind, u8),
245 Prefab(Box<PrefabStructure>, Vec3<i32>, u32),
249 Sampling(Arc<dyn Fn(Vec3<i32>) -> Option<Block>>),
250}
251
252impl Fill {
253 pub fn sprite(kind: SpriteKind) -> Self { Fill::Block(Block::empty().with_sprite(kind)) }
254
255 pub fn sprite_ori(kind: SpriteKind, ori: u8) -> Self {
256 let block = Block::empty().with_sprite(kind);
257
258 let block = block.with_ori(ori).unwrap_or(block);
259 Fill::Sprite(block)
260 }
261
262 pub fn resource_sprite(kind: SpriteKind) -> Self {
263 Fill::ResourceSprite(Block::empty().with_sprite(kind))
264 }
265
266 pub fn resource_sprite_ori(kind: SpriteKind, ori: u8) -> Self {
267 let block = Block::empty().with_sprite(kind);
268
269 let block = block.with_ori(ori).unwrap_or(block);
270 Fill::ResourceSprite(block)
271 }
272
273 pub fn owned_resource_sprite_ori(kind: SpriteKind, ori: u8) -> Self {
274 let block = Block::empty().with_sprite(kind);
275
276 let block = block.with_ori(ori).unwrap_or(block);
277 let block = block
278 .with_attr(common::terrain::sprite::Owned(true))
279 .unwrap_or(block);
280
281 Fill::ResourceSprite(block)
282 }
283
284 pub fn sprite_ori_cfg(kind: SpriteKind, ori: u8, cfg: SpriteCfg) -> Self {
285 let block = Block::empty().with_sprite(kind);
286
287 let block = block.with_ori(ori).unwrap_or(block);
288 Fill::CfgSprite(block, cfg)
289 }
290
291 fn contains_at(
292 tree: &Store<Primitive>,
293 prim: Id<Primitive>,
294 pos: Vec3<i32>,
295 col: &ColInfo,
296 ) -> bool {
297 let aabb_contains = |aabb: Aabb<i32>, pos: Vec3<i32>| {
299 (aabb.min.x..aabb.max.x).contains(&pos.x)
300 && (aabb.min.y..aabb.max.y).contains(&pos.y)
301 && (aabb.min.z..aabb.max.z).contains(&pos.z)
302 };
303
304 match &tree[prim] {
305 Primitive::Empty => false,
306
307 Primitive::Aabb(aabb) => aabb_contains(*aabb, pos),
308 Primitive::Ramp { aabb, inset, dir } => {
309 let inset = (*inset).max(aabb.size().reduce_min());
310 let inner = match dir {
311 Dir::X => Aabr {
312 min: Vec2::new(aabb.min.x - 1 + inset, aabb.min.y),
313 max: Vec2::new(aabb.max.x, aabb.max.y),
314 },
315 Dir::NegX => Aabr {
316 min: Vec2::new(aabb.min.x, aabb.min.y),
317 max: Vec2::new(aabb.max.x - inset, aabb.max.y),
318 },
319 Dir::Y => Aabr {
320 min: Vec2::new(aabb.min.x, aabb.min.y - 1 + inset),
321 max: Vec2::new(aabb.max.x, aabb.max.y),
322 },
323 Dir::NegY => Aabr {
324 min: Vec2::new(aabb.min.x, aabb.min.y),
325 max: Vec2::new(aabb.max.x, aabb.max.y - inset),
326 },
327 };
328 aabb_contains(*aabb, pos)
329 && inner.is_valid()
330 && (inner.projected_point(pos.xy()) - pos.xy())
331 .map(|e| e.abs())
332 .reduce_max() as f32
333 / (inset as f32)
334 < 1.0
335 - ((pos.z - aabb.min.z) as f32 + 0.5) / (aabb.max.z - aabb.min.z) as f32
336 },
337 Primitive::Pyramid { aabb, inset } => {
338 let inset = (*inset).max(aabb.size().reduce_min());
339 let inner = Aabr {
340 min: aabb.min.xy() - 1 + inset,
341 max: aabb.max.xy() - inset,
342 };
343 aabb_contains(*aabb, pos)
344 && inner.is_valid()
345 && (inner.projected_point(pos.xy()) - pos.xy())
346 .map(|e| e.abs())
347 .reduce_max() as f32
348 / (inset as f32)
349 < 1.0
350 - ((pos.z - aabb.min.z) as f32 + 0.5) / (aabb.max.z - aabb.min.z) as f32
351 },
352 Primitive::Gable { aabb, inset, dir } => {
353 let inset = (*inset).max(aabb.size().reduce_min());
354 let inner = if dir.is_y() {
355 Aabr {
356 min: Vec2::new(aabb.min.x - 1 + inset, aabb.min.y),
357 max: Vec2::new(aabb.max.x - inset, aabb.max.y),
358 }
359 } else {
360 Aabr {
361 min: Vec2::new(aabb.min.x, aabb.min.y - 1 + inset),
362 max: Vec2::new(aabb.max.x, aabb.max.y - inset),
363 }
364 };
365 aabb_contains(*aabb, pos)
366 && inner.is_valid()
367 && (inner.projected_point(pos.xy()) - pos.xy())
368 .map(|e| e.abs())
369 .reduce_max() as f32
370 / (inset as f32)
371 < 1.0
372 - ((pos.z - aabb.min.z) as f32 + 0.5) / (aabb.max.z - aabb.min.z) as f32
373 },
374 Primitive::Cylinder(aabb) => {
375 let fpos = pos.as_::<f32>().xy() - aabb.as_::<f32>().center().xy() + 0.5;
377 let size = Vec3::from(aabb.size().as_::<f32>()).xy();
378 (aabb.min.z..aabb.max.z).contains(&pos.z)
379 && (2.0 * fpos / size).magnitude_squared() <= 1.0
380 },
381 Primitive::Cone(aabb) => {
382 (aabb.min.z..aabb.max.z).contains(&pos.z)
383 && pos
384 .xy()
385 .as_()
386 .distance_squared(aabb.as_().center().xy() - 0.5)
387 < (((aabb.max.z - pos.z) as f32 / aabb.size().d as f32)
388 * (aabb.size().w.min(aabb.size().h) as f32 / 2.0))
389 .powi(2)
390 },
391 Primitive::Sphere(aabb) => {
392 aabb_contains(*aabb, pos)
393 && pos.as_().distance_squared(aabb.as_().center() - 0.5)
394 < (aabb.size().w.min(aabb.size().h) as f32 / 2.0).powi(2)
395 },
396 Primitive::Superquadric { aabb, degree } => {
397 let degree = degree.max(0.0);
398 let center = aabb.center().map(|e| e as f32);
399 let a: f32 = aabb.max.x as f32 - center.x - 0.5;
400 let b: f32 = aabb.max.y as f32 - center.y - 0.5;
401 let c: f32 = aabb.max.z as f32 - center.z - 0.5;
402 let rpos = pos.as_::<f32>() + 0.5 - center;
403 aabb_contains(*aabb, pos)
404 && (rpos.x / a).abs().powf(degree)
405 + (rpos.y / b).abs().powf(degree)
406 + (rpos.z / c).abs().powf(degree)
407 < 1.0
408 },
409 Primitive::Plane(aabr, origin, gradient) => {
410 (aabr.min.x..aabr.max.x).contains(&pos.x)
412 && (aabr.min.y..aabr.max.y).contains(&pos.y)
413 && pos.z
414 == origin.z
415 + ((pos.xy() - origin.xy())
416 .map(|x| x.abs())
417 .as_()
418 .dot(*gradient) as i32)
419 },
420 Primitive::Segment { segment, r0, r1 } => {
422 let distance = segment.end - segment.start;
423 let length = pos - segment.start.as_();
424 let t =
425 (length.as_().dot(distance) / distance.magnitude_squared()).clamped(0.0, 1.0);
426 segment.distance_to_point(pos.map(|e| e as f32)) < Lerp::lerp(r0, r1, t) - 0.25
427 },
428 Primitive::SegmentPrism {
429 segment,
430 radius,
431 height,
432 } => {
433 let segment_2d = LineSegment2 {
434 start: segment.start.xy(),
435 end: segment.end.xy(),
436 };
437 let projected_point_2d: Vec2<f32> =
438 segment_2d.as_().projected_point(pos.xy().as_());
439 let xy_check = projected_point_2d.distance(pos.xy().as_()) < radius - 0.25;
440 let projected_z = {
441 let len_sq: f32 = segment_2d
442 .start
443 .as_()
444 .distance_squared(segment_2d.end.as_());
445 if len_sq < 0.1 {
446 segment.start.z
447 } else {
448 let frac = ((projected_point_2d - segment_2d.start.as_())
449 .dot(segment_2d.end.as_() - segment_2d.start.as_())
450 / len_sq)
451 .clamp(0.0, 1.0);
452 (segment.end.z - segment.start.z) * frac + segment.start.z
453 }
454 };
455 let z_check = (projected_z..=(projected_z + height)).contains(&(pos.z as f32));
456 xy_check && z_check
457 },
458 Primitive::Sampling(a, f) => Self::contains_at(tree, *a, pos, col) && f(pos),
459 Primitive::ColSampling(a, f) => Self::contains_at(tree, *a, pos, col) && f(pos, col),
460 Primitive::Prefab(p) => !matches!(p.get(pos), Err(_) | Ok(StructureBlock::None)),
461 Primitive::Intersect(a, b) => {
462 Self::contains_at(tree, *a, pos, col) && Self::contains_at(tree, *b, pos, col)
463 },
464 Primitive::Union(a, b) => {
465 Self::contains_at(tree, *a, pos, col) || Self::contains_at(tree, *b, pos, col)
466 },
467 Primitive::Without(a, b) => {
468 Self::contains_at(tree, *a, pos, col) && !Self::contains_at(tree, *b, pos, col)
469 },
470 Primitive::Translate(prim, vec) => {
471 Self::contains_at(tree, *prim, pos.map2(*vec, i32::saturating_sub), col)
472 },
473 Primitive::Scale(prim, vec) => {
474 let center = Self::get_bounds(tree, *prim).as_::<f32>().center();
475 let fpos = pos.as_::<f32>();
476 let spos = (center + ((fpos - center) / vec))
477 .map(|x| x.round())
478 .as_::<i32>();
479 Self::contains_at(tree, *prim, spos, col)
480 },
481 Primitive::RotateAbout(prim, mat, vec) => {
482 let mat = mat.as_::<f32>().transposed();
483 let vec = vec - 0.5;
484 Self::contains_at(
485 tree,
486 *prim,
487 (vec + mat * (pos.as_::<f32>() - vec)).as_(),
488 col,
489 )
490 },
491 Primitive::Repeat(prim, offset, count) => {
492 if count == &0 {
493 false
494 } else {
495 let count = count - 1;
496 let aabb = Self::get_bounds(tree, *prim);
497 let aabb_corner = {
498 let min_red = aabb.min.map2(*offset, |a, b| if b < 0 { 0 } else { a });
499 let max_red = aabb.max.map2(*offset, |a, b| if b < 0 { a } else { 0 });
500 min_red + max_red
501 };
502 let diff = pos - aabb_corner;
503 let min = diff
504 .map2(*offset, |a, b| if b == 0 { i32::MAX } else { a / b })
505 .reduce_min()
506 .clamp(0, count as i32);
507 let pos = pos - offset * min;
508 Self::contains_at(tree, *prim, pos, col)
509 }
510 },
511 }
512 }
513
514 pub fn sample_at(
519 &self,
520 tree: &Store<Primitive>,
521 prim: Id<Primitive>,
522 pos: Vec3<i32>,
523 canvas_info: &CanvasInfo,
524 old_block: Block,
525 sprite_cfg: &mut Option<SpriteCfg>,
526 col: &ColInfo,
527 ) -> (Option<Block>, Option<StructureBlock>, Option<String>) {
528 if Self::contains_at(tree, prim, pos, col) {
529 match self {
530 Fill::Sprite(sprite) | Fill::ResourceSprite(sprite) => (
531 Some(if old_block.is_filled() {
532 *sprite
533 } else {
534 old_block.with_data_of(*sprite)
535 }),
536 None,
537 None,
538 ),
539 Fill::CfgSprite(sprite, cfg) => {
540 *sprite_cfg = Some(cfg.clone());
541 (
542 Some(if old_block.is_filled() {
543 *sprite
544 } else {
545 old_block.with_data_of(*sprite)
546 }),
547 None,
548 None,
549 )
550 },
551 Fill::Block(block) => (Some(*block), None, None),
552 Fill::Brick(bk, col, range) => {
553 let pos = (pos + Vec3::new(pos.z, pos.z, 0)) / Vec3::new(2, 2, 1);
554 (
555 Some(Block::new(
556 *bk,
557 *col + ((((pos.x ^ pos.y ^ pos.z) as u8).reverse_bits() as u16
558 * *range as u16)
559 >> 8) as u8,
560 )),
564 None,
565 None,
566 )
567 },
568 Fill::PlankWall(bk, col, range) => (
569 Some(Block::new(
570 *bk,
571 *col + (RandomField::new(13)
572 .get((pos + Vec3::new(pos.z, pos.z, 0) * 8) / Vec3::new(16, 16, 1))
573 % *range as u32) as u8,
574 )),
575 None,
576 None,
577 ),
578 Fill::Gradient(gradient, bk) => (
579 Some(Block::new(*bk, gradient.sample(pos.as_()))),
580 None,
581 None,
582 ),
583 Fill::GradientBrick(gradient, bk, range) => {
584 let col = gradient.sample(pos.as_());
585 let pos = (pos + Vec3::new(pos.z, pos.z, 0)) / Vec3::new(2, 2, 1);
586 (
587 Some(Block::new(
588 *bk,
589 col + ((((pos.x ^ pos.y ^ pos.z) as u8).reverse_bits() as u16
590 * *range as u16)
591 >> 8) as u8,
592 )),
593 None,
594 None,
595 )
596 },
597 Fill::Prefab(p, tr, seed) => {
598 let sb_result = p.get(pos - tr);
599 if sb_result.is_err() {
600 return (None, None, None);
601 }
602
603 let sb = sb_result.unwrap();
604
605 let col_sample = canvas_info.col(canvas_info.wpos);
606 if col_sample.is_none() {
607 return (None, None, None);
608 }
609
610 match block_from_structure(
611 canvas_info.index,
612 sb,
613 pos - tr,
614 p.get_bounds().center().xy(),
615 *seed,
616 col_sample.unwrap(),
617 || Block::air(SpriteKind::Empty),
618 canvas_info.calendar(),
619 &Vec2::new(Vec2::new(1, 0), Vec2::new(0, 1)),
620 ) {
621 Some((block, cfg, entity_path)) => {
622 *sprite_cfg = cfg;
623 if let Some(entity_path_str) = entity_path {
624 (
625 Some(block),
626 Some(sb.clone()),
627 Some(entity_path_str.to_string()),
628 )
629 } else {
630 (Some(block), Some(sb.clone()), None)
631 }
632 },
633 _ => (None, None, None),
634 }
635 },
636 Fill::Sampling(f) => (f(pos), None, None),
637 }
638 } else {
639 (None, None, None)
640 }
641 }
642
643 fn get_bounds_inner(tree: &Store<Primitive>, prim: Id<Primitive>) -> Vec<Aabb<i32>> {
644 fn or_zip_with<T, F: FnOnce(T, T) -> T>(a: Option<T>, b: Option<T>, f: F) -> Option<T> {
645 match (a, b) {
646 (Some(a), Some(b)) => Some(f(a, b)),
647 (Some(a), _) => Some(a),
648 (_, b) => b,
649 }
650 }
651
652 match &tree[prim] {
653 Primitive::Empty => vec![],
654 Primitive::Aabb(aabb) => vec![*aabb],
655 Primitive::Pyramid { aabb, .. } => vec![*aabb],
656 Primitive::Gable { aabb, .. } => vec![*aabb],
657 Primitive::Ramp { aabb, .. } => vec![*aabb],
658 Primitive::Cylinder(aabb) => vec![*aabb],
659 Primitive::Cone(aabb) => vec![*aabb],
660 Primitive::Sphere(aabb) => vec![*aabb],
661 Primitive::Superquadric { aabb, .. } => vec![*aabb],
662 Primitive::Plane(aabr, origin, gradient) => {
663 let half_size = aabr.half_size().reduce_max();
664 let longest_dist = ((aabr.center() - origin.xy()).map(|x| x.abs())
665 + half_size
666 + aabr.size().reduce_max() % 2)
667 .map(|x| x as f32);
668 let z = if gradient.x.signum() == gradient.y.signum() {
669 Vec2::new(0, longest_dist.dot(*gradient) as i32)
670 } else {
671 (longest_dist * gradient).as_()
672 };
673 let aabb = Aabb {
674 min: aabr.min.with_z(origin.z + z.reduce_min().min(0)),
675 max: aabr.max.with_z(origin.z + z.reduce_max().max(0)),
676 };
677 vec![aabb.made_valid()]
678 },
679 Primitive::Segment { segment, r0, r1 } => {
680 let aabb = Aabb {
681 min: segment.start,
682 max: segment.end,
683 }
684 .made_valid();
685 vec![Aabb {
686 min: (aabb.min - r0.max(*r1)).floor().as_(),
687 max: (aabb.max + r0.max(*r1)).ceil().as_(),
688 }]
689 },
690 Primitive::SegmentPrism {
691 segment,
692 radius,
693 height,
694 } => {
695 let aabb = Aabb {
696 min: segment.start,
697 max: segment.end,
698 }
699 .made_valid();
700 let min = {
701 let xy = (aabb.min.xy() - *radius).floor();
702 xy.with_z(aabb.min.z).as_()
703 };
704 let max = {
705 let xy = (aabb.max.xy() + *radius).ceil();
706 xy.with_z((aabb.max.z + *height).ceil()).as_()
707 };
708 vec![Aabb { min, max }]
709 },
710 Primitive::Sampling(a, _) | Primitive::ColSampling(a, _) => {
711 Self::get_bounds_inner(tree, *a)
712 },
713 Primitive::Prefab(p) => vec![p.get_bounds()],
714 Primitive::Intersect(a, b) => or_zip_with(
715 Self::get_bounds_opt(tree, *a),
716 Self::get_bounds_opt(tree, *b),
717 |a, b| a.intersection(b),
718 )
719 .into_iter()
720 .collect(),
721
722 Primitive::Union(a, b) => {
723 fn jaccard(x: Aabb<i32>, y: Aabb<i32>) -> f32 {
724 let s_intersection = x.intersection(y).size().as_::<f32>().magnitude();
725 let s_union = x.union(y).size().as_::<f32>().magnitude();
726 s_intersection / s_union
727 }
728 let mut inputs = Vec::new();
729 inputs.extend(Self::get_bounds_inner(tree, *a));
730 inputs.extend(Self::get_bounds_inner(tree, *b));
731 let mut results = Vec::new();
732 if let Some(aabb) = inputs.pop() {
733 results.push(aabb);
734 for a in &inputs {
735 let best = results
736 .iter()
737 .enumerate()
738 .max_by_key(|(_, b)| (jaccard(*a, **b) * 1000.0) as usize);
739 match best {
740 Some((i, b)) if jaccard(*a, *b) > 0.3 => {
741 let mut aabb = results.swap_remove(i);
742 aabb = aabb.union(*a);
743 results.push(aabb);
744 },
745 _ => results.push(*a),
746 }
747 }
748 results
749 } else {
750 results
751 }
752 },
753 Primitive::Without(a, _) => Self::get_bounds_inner(tree, *a),
754 Primitive::Translate(prim, vec) => Self::get_bounds_inner(tree, *prim)
755 .into_iter()
756 .map(|aabb| Aabb {
757 min: aabb.min.map2(*vec, i32::saturating_add),
758 max: aabb.max.map2(*vec, i32::saturating_add),
759 })
760 .collect(),
761 Primitive::Scale(prim, vec) => Self::get_bounds_inner(tree, *prim)
762 .into_iter()
763 .map(|aabb| {
764 let center = aabb.center();
765 Aabb {
766 min: center + ((aabb.min - center).as_::<f32>() * vec).as_::<i32>(),
767 max: center + ((aabb.max - center).as_::<f32>() * vec).as_::<i32>(),
768 }
769 })
770 .collect(),
771 Primitive::RotateAbout(prim, mat, vec) => Self::get_bounds_inner(tree, *prim)
772 .into_iter()
773 .map(|aabb| {
774 let mat = mat.as_::<f32>();
775 let vec = vec - 0.5;
777 let new_aabb = Aabb::<f32> {
778 min: vec + mat * (aabb.min.as_() - vec),
779 max: vec + mat * ((aabb.max - 1).as_() - vec),
782 }
783 .made_valid();
784 Aabb::<i32> {
785 min: new_aabb.min.as_(),
786 max: new_aabb.max.as_() + 1,
787 }
788 })
789 .collect(),
790 Primitive::Repeat(prim, offset, count) => {
791 if count == &0 {
792 vec![]
793 } else {
794 let count = count - 1;
795 Self::get_bounds_inner(tree, *prim)
796 .into_iter()
797 .map(|aabb| Aabb {
798 min: aabb
799 .min
800 .map2(aabb.min + offset * count as i32, |a, b| a.min(b)),
801 max: aabb
802 .max
803 .map2(aabb.max + offset * count as i32, |a, b| a.max(b)),
804 })
805 .collect()
806 }
807 },
808 }
809 }
810
811 pub fn get_bounds_disjoint(tree: &Store<Primitive>, prim: Id<Primitive>) -> Vec<Aabb<i32>> {
812 Self::get_bounds_inner(tree, prim)
813 }
814
815 pub fn get_bounds_opt(tree: &Store<Primitive>, prim: Id<Primitive>) -> Option<Aabb<i32>> {
816 Self::get_bounds_inner(tree, prim)
817 .into_iter()
818 .reduce(|a, b| a.union(b))
819 }
820
821 pub fn get_bounds(tree: &Store<Primitive>, prim: Id<Primitive>) -> Aabb<i32> {
822 Self::get_bounds_opt(tree, prim).unwrap_or_else(|| Aabb::new_empty(Vec3::zero()))
823 }
824}
825
826pub struct Painter {
827 prims: RefCell<Store<Primitive>>,
828 fills: RefCell<Vec<(Id<Primitive>, Fill)>>,
829 entities: RefCell<Vec<EntityInfo>>,
830 render_area: Aabr<i32>,
831}
832
833impl Painter {
834 pub fn depth(&self, prim: Id<Primitive>) -> usize {
836 fn aux(prims: &Store<Primitive>, prim: Id<Primitive>, prev_depth: usize) -> usize {
837 match prims[prim] {
838 Primitive::Empty
839 | Primitive::Aabb(_)
840 | Primitive::Pyramid { .. }
841 | Primitive::Ramp { .. }
842 | Primitive::Gable { .. }
843 | Primitive::Cylinder(_)
844 | Primitive::Cone(_)
845 | Primitive::Sphere(_)
846 | Primitive::Superquadric { .. }
847 | Primitive::Plane(_, _, _)
848 | Primitive::Segment { .. }
849 | Primitive::SegmentPrism { .. }
850 | Primitive::Prefab(_) => prev_depth,
851 Primitive::Sampling(a, _)
852 | Primitive::ColSampling(a, _)
853 | Primitive::Translate(a, _)
854 | Primitive::Scale(a, _)
855 | Primitive::RotateAbout(a, _, _)
856 | Primitive::Repeat(a, _, _) => aux(prims, a, 1 + prev_depth),
857
858 Primitive::Intersect(a, b) | Primitive::Union(a, b) | Primitive::Without(a, b) => {
859 aux(prims, a, 1 + prev_depth).max(aux(prims, b, 1 + prev_depth))
860 },
861 }
862 }
863 let prims = self.prims.borrow();
864 aux(&prims, prim, 0)
865 }
866
867 pub fn order_by_depth(
870 &self,
871 a: impl Into<Id<Primitive>>,
872 b: impl Into<Id<Primitive>>,
873 ) -> (Id<Primitive>, Id<Primitive>) {
874 let (a, b) = (a.into(), b.into());
875 if self.depth(a) < self.depth(b) {
876 (a, b)
877 } else {
878 (b, a)
879 }
880 }
881
882 pub fn aabb(&self, aabb: Aabb<i32>) -> PrimitiveRef<'_> {
885 self.prim(Primitive::Aabb(aabb.made_valid()))
886 }
887
888 pub fn sphere(&self, aabb: Aabb<i32>) -> PrimitiveRef<'_> {
890 self.prim(Primitive::Sphere(aabb.made_valid()))
891 }
892
893 pub fn sphere_with_radius(&self, origin: Vec3<i32>, radius: f32) -> PrimitiveRef<'_> {
896 let min = origin - Vec3::broadcast(radius.round() as i32);
897 let max = origin + Vec3::broadcast(radius.round() as i32);
898 self.prim(Primitive::Sphere(Aabb { min, max }))
899 }
900
901 pub fn sphere2(&self, aabb: Aabb<i32>) -> PrimitiveRef<'_> {
905 let aabb = aabb.made_valid();
906 let radius = aabb.size().w.min(aabb.size().h) / 2;
907 let aabb = Aabb {
908 min: aabb.center() - radius,
909 max: aabb.center() + radius,
910 };
911 let degree = 2.0;
912 self.prim(Primitive::Superquadric { aabb, degree })
913 }
914
915 pub fn ellipsoid(&self, aabb: Aabb<i32>) -> PrimitiveRef<'_> {
918 let aabb = aabb.made_valid();
919 let degree = 2.0;
920 self.prim(Primitive::Superquadric { aabb, degree })
921 }
922
923 pub fn superquadric(&self, aabb: Aabb<i32>, degree: f32) -> PrimitiveRef<'_> {
933 let aabb = aabb.made_valid();
934 self.prim(Primitive::Superquadric { aabb, degree })
935 }
936
937 pub fn rounded_aabb(&self, aabb: Aabb<i32>) -> PrimitiveRef<'_> {
940 let aabb = aabb.made_valid();
941 self.prim(Primitive::Superquadric { aabb, degree: 3.0 })
942 }
943
944 pub fn cylinder(&self, aabb: Aabb<i32>) -> PrimitiveRef<'_> {
947 self.prim(Primitive::Cylinder(aabb.made_valid()))
948 }
949
950 pub fn horizontal_cylinder(&self, aabb: Aabb<i32>, dir: Dir) -> PrimitiveRef<'_> {
953 let aabr = Aabr::from(aabb);
954 let length = dir.select(aabr.size());
955 let height = aabb.max.z - aabb.min.z;
956 let aabb = Aabb {
957 min: (aabr.min - dir.abs().to_vec2() * height).with_z(aabb.min.z),
958 max: (dir.abs().select_with(aabr.min, aabr.max)).with_z(aabb.min.z + length),
959 };
960 self.cylinder(aabb)
961 .rotate_about((-dir.abs()).from_z_mat3(), aabr.min.with_z(aabb.min.z))
962 }
963
964 pub fn cylinder_with_radius(
967 &self,
968 origin: Vec3<i32>,
969 radius: f32,
970 height: f32,
971 ) -> PrimitiveRef<'_> {
972 let min = origin - Vec2::broadcast(radius.round() as i32);
973 let max = origin + Vec2::broadcast(radius.round() as i32).with_z(height.round() as i32);
974 self.prim(Primitive::Cylinder(Aabb { min, max }))
975 }
976
977 pub fn cone(&self, aabb: Aabb<i32>) -> PrimitiveRef<'_> {
980 self.prim(Primitive::Cone(aabb.made_valid()))
981 }
982
983 pub fn cone_with_radius(
986 &self,
987 origin: Vec3<i32>,
988 radius: f32,
989 height: f32,
990 ) -> PrimitiveRef<'_> {
991 let min = origin - Vec2::broadcast(radius.round() as i32);
992 let max = origin + Vec2::broadcast(radius.round() as i32).with_z(height.round() as i32);
993 self.prim(Primitive::Cone(Aabb { min, max }))
994 }
995
996 pub fn line(
999 &self,
1000 a: Vec3<impl AsPrimitive<f32>>,
1001 b: Vec3<impl AsPrimitive<f32>>,
1002 radius: f32,
1003 ) -> PrimitiveRef<'_> {
1004 self.prim(Primitive::Segment {
1005 segment: LineSegment3 {
1006 start: a.as_(),
1007 end: b.as_(),
1008 },
1009 r0: radius,
1010 r1: radius,
1011 })
1012 }
1013
1014 pub fn line_two_radius(
1017 &self,
1018 a: Vec3<impl AsPrimitive<f32>>,
1019 b: Vec3<impl AsPrimitive<f32>>,
1020 r0: f32,
1021 r1: f32,
1022 ) -> PrimitiveRef<'_> {
1023 self.prim(Primitive::Segment {
1024 segment: LineSegment3 {
1025 start: a.as_(),
1026 end: b.as_(),
1027 },
1028 r0,
1029 r1,
1030 })
1031 }
1032
1033 pub fn segment_prism(
1039 &self,
1040 a: Vec3<impl AsPrimitive<f32>>,
1041 b: Vec3<impl AsPrimitive<f32>>,
1042 radius: f32,
1043 height: f32,
1044 ) -> PrimitiveRef<'_> {
1045 let segment = LineSegment3 {
1046 start: a.as_(),
1047 end: b.as_(),
1048 };
1049 self.prim(Primitive::SegmentPrism {
1050 segment,
1051 radius,
1052 height,
1053 })
1054 }
1055
1056 pub fn cubic_bezier(
1060 &self,
1061 start: Vec3<impl AsPrimitive<f32>>,
1062 ctrl0: Vec3<impl AsPrimitive<f32>>,
1063 ctrl1: Vec3<impl AsPrimitive<f32>>,
1064 end: Vec3<impl AsPrimitive<f32>>,
1065 radius: f32,
1066 ) -> PrimitiveRef<'_> {
1067 let bezier = CubicBezier3 {
1068 start: start.as_(),
1069 ctrl0: ctrl0.as_(),
1070 ctrl1: ctrl1.as_(),
1071 end: end.as_(),
1072 };
1073 let length = bezier.length_by_discretization(10);
1074 let num_segments = (0.2 * length).ceil() as u16;
1075 self.cubic_bezier_with_num_segments(bezier, radius, num_segments)
1076 }
1077
1078 pub fn cubic_bezier_with_num_segments(
1081 &self,
1082 bezier: CubicBezier3<f32>,
1083 radius: f32,
1084 num_segments: u16,
1085 ) -> PrimitiveRef<'_> {
1086 let mut bezier_prim = self.empty();
1087 let range: Vec<_> = (0..=num_segments).collect();
1088 range.windows(2).for_each(|w| {
1089 let segment_start = bezier.evaluate(w[0] as f32 / num_segments as f32);
1090 let segment_end = bezier.evaluate(w[1] as f32 / num_segments as f32);
1091 bezier_prim = bezier_prim.union(self.line(segment_start, segment_end, radius));
1092 });
1093 bezier_prim
1094 }
1095
1096 pub fn cubic_bezier_prism(
1103 &self,
1104 start: Vec3<impl AsPrimitive<f32>>,
1105 ctrl0: Vec3<impl AsPrimitive<f32>>,
1106 ctrl1: Vec3<impl AsPrimitive<f32>>,
1107 end: Vec3<impl AsPrimitive<f32>>,
1108 radius: f32,
1109 height: f32,
1110 ) -> PrimitiveRef<'_> {
1111 let bezier = CubicBezier3 {
1112 start: start.as_(),
1113 ctrl0: ctrl0.as_(),
1114 ctrl1: ctrl1.as_(),
1115 end: end.as_(),
1116 };
1117 let length = bezier.length_by_discretization(10);
1118 let num_segments = (0.2 * length).ceil() as u16;
1119 self.cubic_bezier_prism_with_num_segments(bezier, radius, height, num_segments)
1120 }
1121
1122 pub fn cubic_bezier_prism_with_num_segments(
1128 &self,
1129 bezier: CubicBezier3<f32>,
1130 radius: f32,
1131 height: f32,
1132 num_segments: u16,
1133 ) -> PrimitiveRef<'_> {
1134 let mut bezier_prim = self.empty();
1135 let range: Vec<_> = (0..=num_segments).collect();
1136 range.windows(2).for_each(|w| {
1137 let segment_start = bezier.evaluate(w[0] as f32 / num_segments as f32);
1138 let segment_end = bezier.evaluate(w[1] as f32 / num_segments as f32);
1139 bezier_prim =
1140 bezier_prim.union(self.segment_prism(segment_start, segment_end, radius, height));
1141 });
1142 bezier_prim
1143 }
1144
1145 pub fn plane(
1150 &self,
1151 aabr: Aabr<i32>,
1152 origin: Vec3<i32>,
1153 gradient: Vec2<f32>,
1154 ) -> PrimitiveRef<'_> {
1155 let aabr = aabr.made_valid();
1156 self.prim(Primitive::Plane(aabr, origin, gradient))
1157 }
1158
1159 pub fn ramp_inset(&self, aabb: Aabb<i32>, inset: i32, dir: Dir) -> PrimitiveRef<'_> {
1163 let aabb = aabb.made_valid();
1164 self.prim(Primitive::Ramp { aabb, inset, dir })
1165 }
1166
1167 pub fn ramp(&self, aabb: Aabb<i32>, dir: Dir) -> PrimitiveRef<'_> {
1168 let aabb = aabb.made_valid();
1169 self.prim(Primitive::Ramp {
1170 aabb,
1171 inset: dir.select((aabb.size().w, aabb.size().h)),
1172 dir,
1173 })
1174 }
1175
1176 pub fn gable(&self, aabb: Aabb<i32>, inset: i32, dir: Dir) -> PrimitiveRef<'_> {
1180 let aabb = aabb.made_valid();
1181 self.prim(Primitive::Gable { aabb, inset, dir })
1182 }
1183
1184 pub fn sprite(&self, pos: Vec3<i32>, sprite: SpriteKind) {
1186 self.aabb(Aabb {
1187 min: pos,
1188 max: pos + 1,
1189 })
1190 .fill(Fill::sprite(sprite))
1191 }
1192
1193 pub fn rotated_sprite(&self, pos: Vec3<i32>, sprite: SpriteKind, ori: u8) {
1195 self.aabb(Aabb {
1196 min: pos,
1197 max: pos + 1,
1198 })
1199 .fill(Fill::sprite_ori(sprite, ori % 8))
1200 }
1201
1202 pub fn rotated_sprite_with_cfg(
1205 &self,
1206 pos: Vec3<i32>,
1207 sprite: SpriteKind,
1208 ori: u8,
1209 cfg: SpriteCfg,
1210 ) {
1211 self.aabb(Aabb {
1212 min: pos,
1213 max: pos + 1,
1214 })
1215 .fill(Fill::sprite_ori_cfg(sprite, ori % 8, cfg))
1216 }
1217
1218 pub fn resource_sprite(&self, pos: Vec3<i32>, sprite: SpriteKind, ori: u8) {
1222 self.aabb(Aabb {
1223 min: pos,
1224 max: pos + 1,
1225 })
1226 .fill(Fill::resource_sprite_ori(sprite, ori % 8))
1227 }
1228
1229 pub fn owned_resource_sprite(&self, pos: Vec3<i32>, sprite: SpriteKind, ori: u8) {
1233 self.aabb(Aabb {
1234 min: pos,
1235 max: pos + 1,
1236 })
1237 .fill(Fill::owned_resource_sprite_ori(sprite, ori))
1238 }
1239
1240 pub fn pyramid(&self, aabb: Aabb<i32>) -> PrimitiveRef<'_> {
1243 let inset = 0;
1244 let aabb = aabb.made_valid();
1245 self.prim(Primitive::Ramp {
1246 aabb,
1247 inset,
1248 dir: Dir::X,
1249 })
1250 .intersect(self.prim(Primitive::Ramp {
1251 aabb,
1252 inset,
1253 dir: Dir::NegX,
1254 }))
1255 .intersect(self.prim(Primitive::Ramp {
1256 aabb,
1257 inset,
1258 dir: Dir::Y,
1259 }))
1260 .intersect(self.prim(Primitive::Ramp {
1261 aabb,
1262 inset,
1263 dir: Dir::NegY,
1264 }))
1265 }
1266
1267 pub fn prim(&self, prim: Primitive) -> PrimitiveRef<'_> {
1270 PrimitiveRef {
1271 id: self.prims.borrow_mut().insert(prim),
1272 painter: self,
1273 }
1274 }
1275
1276 pub fn empty(&self) -> PrimitiveRef<'_> { self.prim(Primitive::Empty) }
1279
1280 pub fn fill(&self, prim: impl Into<Id<Primitive>>, fill: Fill) {
1282 let prim = prim.into();
1283 if let Primitive::Union(a, b) = self.prims.borrow()[prim] {
1284 self.fill(a, fill.clone());
1285 self.fill(b, fill);
1286 } else {
1287 self.fills.borrow_mut().push((prim, fill));
1288 }
1289 }
1290
1291 pub fn vault(&self, aabb: Aabb<i32>, dir: Dir) -> PrimitiveRef<'_> {
1301 let h = dir.orthogonal().select(Vec3::from(aabb.size()).xy());
1302
1303 let mut prim = self.horizontal_cylinder(
1304 Aabb {
1305 min: aabb.min.with_z(aabb.max.z - h),
1306 max: aabb.max,
1307 },
1308 dir,
1309 );
1310
1311 if aabb.size().d < h {
1312 prim = prim.intersect(self.aabb(aabb));
1313 }
1314
1315 self.aabb(Aabb {
1316 min: aabb.min,
1317 max: aabb.max.with_z(aabb.max.z - h / 2),
1318 })
1319 .union(prim)
1320 }
1321
1322 pub fn aabbs_around_aabb(&self, aabb: Aabb<i32>, size: i32, offset: i32) -> PrimitiveRef<'_> {
1324 let pillar = self.aabb(Aabb {
1325 min: (aabb.min.xy() - 1).with_z(aabb.min.z),
1326 max: (aabb.min.xy() + size - 1).with_z(aabb.max.z),
1327 });
1328
1329 let true_offset = offset + size;
1330
1331 let size_x = aabb.max.x - aabb.min.x;
1332 let size_y = aabb.max.y - aabb.min.y;
1333
1334 let num_aabbs = ((size_x + 1) / 2) / true_offset;
1335 let x = pillar.repeat(Vec3::new(true_offset, 0, 0), num_aabbs as u32 + 1);
1336
1337 let num_aabbs = ((size_y + 1) / 2) / true_offset;
1338 let y = pillar.repeat(Vec3::new(0, true_offset, 0), num_aabbs as u32 + 1);
1339 let center = aabb.as_::<f32>().center();
1340 let shape = x.union(y);
1341 let shape =
1342 shape.union(shape.rotate_about(Mat3::new(-1, 0, 0, 0, -1, 0, 0, 0, -1), center));
1343 shape.union(shape.rotate_about(Mat3::new(0, 1, 0, -1, 0, 0, 0, 0, 1), center))
1344 }
1345
1346 pub fn staircase_in_aabb(
1347 &self,
1348 aabb: Aabb<i32>,
1349 thickness: i32,
1350 start_dir: Dir,
1351 ) -> PrimitiveRef<'_> {
1352 let mut forward = start_dir;
1353 let mut z = aabb.max.z - 1;
1354 let aabr = Aabr::from(aabb);
1355
1356 let mut prim = self.empty();
1357
1358 while z > aabb.min.z {
1359 let right = forward.rotated_cw();
1360 let fc = forward.select_aabr(aabr);
1361 let corner =
1362 fc * forward.abs().to_vec2() + right.select_aabr(aabr) * right.abs().to_vec2();
1363 let aabb = Aabb {
1364 min: corner.with_z(z),
1365 max: (corner - (forward.to_vec2() + right.to_vec2()) * thickness).with_z(z + 1),
1366 }
1367 .made_valid();
1368
1369 let stair_len = ((fc - (-forward).select_aabr(aabr)).abs() - thickness * 2).max(1) + 1;
1370
1371 let stairs = self.ramp(
1372 Aabb {
1373 min: (corner - right.to_vec2() * (stair_len + thickness)).with_z(z - stair_len),
1374 max: (corner
1375 - right.to_vec2() * (thickness - 1)
1376 - forward.to_vec2() * thickness)
1377 .with_z(z + 1),
1378 }
1379 .made_valid(),
1380 right,
1381 );
1382
1383 prim = prim
1384 .union(self.aabb(aabb))
1385 .union(stairs.without(stairs.translate(Vec3::new(0, 0, -2))));
1386
1387 z -= stair_len;
1388 forward = forward.rotated_ccw();
1389 }
1390 prim
1391 }
1392
1393 pub fn numeral(&self, origin: Vec3<i32>, numeral: usize) -> PrimitiveRef<'_> {
1395 let bottom_bar = self.aabb(Aabb {
1396 min: Vec2::new(origin.x, origin.y).with_z(origin.z),
1397 max: Vec2::new(origin.x + 1, origin.y + 4).with_z(origin.z + 1),
1398 });
1399 let mid_bar = self.aabb(Aabb {
1400 min: Vec2::new(origin.x, origin.y).with_z(origin.z + 2),
1401 max: Vec2::new(origin.x + 1, origin.y + 4).with_z(origin.z + 3),
1402 });
1403 let top_bar = self.aabb(Aabb {
1404 min: Vec2::new(origin.x, origin.y).with_z(origin.z + 4),
1405 max: Vec2::new(origin.x + 1, origin.y + 4).with_z(origin.z + 5),
1406 });
1407 let left_top = self.aabb(Aabb {
1408 min: Vec2::new(origin.x, origin.y).with_z(origin.z + 2),
1409 max: Vec2::new(origin.x + 1, origin.y + 1).with_z(origin.z + 5),
1410 });
1411 let left_bottom = self.aabb(Aabb {
1412 min: Vec2::new(origin.x, origin.y).with_z(origin.z),
1413 max: Vec2::new(origin.x + 1, origin.y + 1).with_z(origin.z + 3),
1414 });
1415 let right_top = self.aabb(Aabb {
1416 min: Vec2::new(origin.x, origin.y + 3).with_z(origin.z + 2),
1417 max: Vec2::new(origin.x + 1, origin.y + 4).with_z(origin.z + 5),
1418 });
1419 let right_bottom = self.aabb(Aabb {
1420 min: Vec2::new(origin.x, origin.y + 3).with_z(origin.z),
1421 max: Vec2::new(origin.x + 1, origin.y + 4).with_z(origin.z + 3),
1422 });
1423 let number_strokes = match numeral {
1424 0 => &[
1425 top_bar,
1426 bottom_bar,
1427 right_top,
1428 right_bottom,
1429 left_top,
1430 left_bottom,
1431 ] as &[_],
1432 1 => &[right_top, right_bottom],
1433 2 => &[top_bar, right_top, mid_bar, left_bottom, bottom_bar],
1434 3 => &[top_bar, bottom_bar, mid_bar, right_top, right_bottom],
1435 4 => &[left_top, mid_bar, right_top, right_bottom],
1436 5 => &[top_bar, left_top, mid_bar, right_bottom, bottom_bar],
1437 6 => &[
1438 top_bar,
1439 left_top,
1440 left_bottom,
1441 bottom_bar,
1442 right_bottom,
1443 mid_bar,
1444 ],
1445 7 => &[top_bar, right_top, right_bottom],
1446 8 => &[
1447 top_bar,
1448 left_top,
1449 left_bottom,
1450 mid_bar,
1451 right_top,
1452 right_bottom,
1453 bottom_bar,
1454 ],
1455 _ => &[top_bar, left_top, mid_bar, right_top, right_bottom],
1456 };
1457
1458 let mut prim = self.empty();
1459 for stroke in number_strokes {
1460 prim = prim.union(*stroke)
1461 }
1462
1463 prim
1464 }
1465
1466 pub fn column(&self, point: Vec2<i32>, range: impl RangeBounds<i32>) -> PrimitiveRef<'_> {
1467 self.aabb(Aabb {
1468 min: point.with_z(match range.start_bound() {
1469 std::ops::Bound::Included(n) => *n,
1470 std::ops::Bound::Excluded(n) => n + 1,
1471 std::ops::Bound::Unbounded => i32::MIN,
1472 }),
1473 max: (point + 1).with_z(match range.end_bound() {
1474 std::ops::Bound::Included(n) => n + 1,
1475 std::ops::Bound::Excluded(n) => *n,
1476 std::ops::Bound::Unbounded => i32::MAX,
1477 }),
1478 })
1479 }
1480
1481 pub fn render_aabr(&self) -> Aabr<i32> { self.render_area }
1483
1484 pub fn spawn(&self, entity: EntityInfo) {
1486 if self.render_area.contains_point(entity.pos.xy().as_()) {
1487 self.entities.borrow_mut().push(entity)
1488 }
1489 }
1490}
1491
1492pub fn render_prefab(file_path: &str, position: Vec3<i32>, painter: &Painter) {
1493 let asset_handle = PrefabStructure::load_group(file_path);
1494 let prefab_structure = asset_handle.read()[0].clone();
1495
1496 painter
1498 .prim(Primitive::Prefab(Box::new(prefab_structure.clone())))
1499 .translate(position)
1500 .fill(Fill::Prefab(Box::new(prefab_structure), position, 0));
1501}
1502
1503#[derive(Copy, Clone)]
1504pub struct PrimitiveRef<'a> {
1505 id: Id<Primitive>,
1506 painter: &'a Painter,
1507}
1508
1509impl<'a> From<PrimitiveRef<'a>> for Id<Primitive> {
1510 fn from(r: PrimitiveRef<'a>) -> Self { r.id }
1511}
1512
1513impl<'a> PrimitiveRef<'a> {
1514 #[must_use]
1517 pub fn union(self, other: impl Into<Id<Primitive>>) -> PrimitiveRef<'a> {
1518 let (a, b) = self.painter.order_by_depth(self, other);
1519 self.painter.prim(Primitive::union(a, b))
1520 }
1521
1522 #[must_use]
1525 pub fn intersect(self, other: impl Into<Id<Primitive>>) -> PrimitiveRef<'a> {
1526 let (a, b) = self.painter.order_by_depth(self, other);
1527 self.painter.prim(Primitive::intersect(a, b))
1528 }
1529
1530 #[must_use]
1533 pub fn without(self, other: impl Into<Id<Primitive>>) -> PrimitiveRef<'a> {
1534 self.painter.prim(Primitive::without(self, other))
1535 }
1536
1537 pub fn fill(self, fill: Fill) { self.painter.fill(self, fill); }
1539
1540 pub fn clear(self) { self.painter.fill(self, Fill::Block(Block::empty())); }
1544
1545 #[must_use]
1548 pub fn sample(self, sampling: impl Fn(Vec3<i32>) -> bool + 'static) -> PrimitiveRef<'a> {
1549 self.painter
1550 .prim(Primitive::sampling(self, Box::new(sampling)))
1551 }
1552
1553 #[must_use]
1556 pub fn sample_with_column(
1557 self,
1558 sampling: impl Fn(Vec3<i32>, &ColInfo) -> bool + 'static,
1559 ) -> PrimitiveRef<'a> {
1560 self.painter
1561 .prim(Primitive::column_sampling(self, Box::new(sampling)))
1562 }
1563
1564 #[must_use]
1566 pub fn rotate_about_min(self, mat: Mat3<i32>) -> PrimitiveRef<'a> {
1567 let point = Fill::get_bounds(&self.painter.prims.borrow(), self.into()).min;
1568 self.rotate_about(mat, point)
1569 }
1570}
1571
1572pub trait PrimitiveTransform {
1574 #[must_use]
1576 fn translate(self, trans: Vec3<i32>) -> Self;
1577 #[must_use]
1580 fn rotate_about(self, rot: Mat3<i32>, point: Vec3<impl AsPrimitive<f32>>) -> Self;
1581 #[must_use]
1584 fn rotate_z_90_about(self, n: i32, point: Vec3<impl AsPrimitive<f32>>) -> Self;
1585 #[expect(dead_code)]
1588 #[must_use]
1589 fn scale(self, scale: Vec3<impl AsPrimitive<f32>>) -> Self;
1590 #[must_use]
1594 fn repeat(self, offset: Vec3<i32>, count: u32) -> Self;
1595}
1596
1597impl PrimitiveTransform for PrimitiveRef<'_> {
1598 fn translate(self, trans: Vec3<i32>) -> Self {
1599 self.painter.prim(Primitive::translate(self, trans))
1600 }
1601
1602 fn rotate_about(self, rot: Mat3<i32>, point: Vec3<impl AsPrimitive<f32>>) -> Self {
1603 self.painter.prim(Primitive::rotate_about(self, rot, point))
1604 }
1605
1606 fn rotate_z_90_about(self, n: i32, point: Vec3<impl AsPrimitive<f32>>) -> Self {
1607 self.painter
1608 .prim(Primitive::rotate_z_90_about(self, n, point))
1609 }
1610
1611 fn scale(self, scale: Vec3<impl AsPrimitive<f32>>) -> Self {
1612 self.painter.prim(Primitive::scale(self, scale.as_()))
1613 }
1614
1615 fn repeat(self, offset: Vec3<i32>, count: u32) -> Self {
1616 self.painter.prim(Primitive::repeat(self, offset, count))
1617 }
1618}
1619
1620impl<const N: usize> PrimitiveTransform for [PrimitiveRef<'_>; N] {
1621 fn translate(mut self, trans: Vec3<i32>) -> Self {
1622 for prim in &mut self {
1623 *prim = prim.translate(trans);
1624 }
1625 self
1626 }
1627
1628 fn rotate_about(mut self, rot: Mat3<i32>, point: Vec3<impl AsPrimitive<f32>>) -> Self {
1629 for prim in &mut self {
1630 *prim = prim.rotate_about(rot, point);
1631 }
1632 self
1633 }
1634
1635 fn rotate_z_90_about(mut self, n: i32, point: Vec3<impl AsPrimitive<f32>>) -> Self {
1636 for prim in &mut self {
1637 *prim = prim.rotate_z_90_about(n, point);
1638 }
1639 self
1640 }
1641
1642 fn scale(mut self, scale: Vec3<impl AsPrimitive<f32>>) -> Self {
1643 for prim in &mut self {
1644 *prim = prim.scale(scale);
1645 }
1646 self
1647 }
1648
1649 fn repeat(mut self, offset: Vec3<i32>, count: u32) -> Self {
1650 for prim in &mut self {
1651 *prim = prim.repeat(offset, count);
1652 }
1653 self
1654 }
1655}
1656
1657pub trait Structure {
1658 #[cfg(feature = "use-dyn-lib")]
1659 const UPDATE_FN: &'static [u8];
1660
1661 fn render_inner(&self, _site: &Site, _land: &Land, _painter: &Painter) {}
1662
1663 fn render(&self, site: &Site, land: &Land, painter: &Painter) {
1664 #[cfg(not(feature = "use-dyn-lib"))]
1665 {
1666 self.render_inner(site, land, painter);
1667 }
1668 #[cfg(feature = "use-dyn-lib")]
1669 {
1670 let lock = LIB.lock().unwrap();
1671 let lib = &lock.as_ref().unwrap().lib;
1672
1673 let update_fn: common_dynlib::Symbol<fn(&Self, &Site, &Land, &Painter)> = unsafe {
1674 lib.get(Self::UPDATE_FN)
1677 }
1679 .unwrap_or_else(|e| {
1680 panic!(
1681 "Trying to use: {} but had error: {:?}",
1682 CStr::from_bytes_with_nul(Self::UPDATE_FN)
1683 .map(CStr::to_str)
1684 .unwrap()
1685 .unwrap(),
1686 e
1687 )
1688 });
1689
1690 update_fn(self, site, land, painter);
1691 }
1692 }
1693
1694 fn render_collect(
1696 &self,
1697 site: &Site,
1698 canvas: &CanvasInfo,
1699 ) -> (
1700 Store<Primitive>,
1701 Vec<(Id<Primitive>, Fill)>,
1702 Vec<EntityInfo>,
1703 ) {
1704 let painter = Painter {
1705 prims: RefCell::new(Store::default()),
1706 fills: RefCell::new(Vec::new()),
1707 entities: RefCell::new(Vec::new()),
1708 render_area: Aabr {
1709 min: canvas.wpos,
1710 max: canvas.wpos + TerrainChunkSize::RECT_SIZE.map(|e| e as i32),
1711 },
1712 };
1713
1714 self.render(site, &canvas.land(), &painter);
1715 (
1716 painter.prims.into_inner(),
1717 painter.fills.into_inner(),
1718 painter.entities.into_inner(),
1719 )
1720 }
1721
1722 fn rel_terrain_offset(&self, col: &ColumnSample) -> i32 { col.alt as i32 }
1725
1726 fn terrain_surface_at<R: Rng>(
1727 &self,
1728 _wpos: Vec2<i32>,
1729 _old: Block,
1730 _rng: &mut R,
1731 _col: &ColumnSample,
1732 _z_off: i32,
1733 _site: &Site,
1734 ) -> Option<Block> {
1735 None
1736 }
1737}
1738
1739pub fn aabr_with_z<T>(aabr: Aabr<T>, z: Range<T>) -> Aabb<T> {
1741 Aabb {
1742 min: aabr.min.with_z(z.start),
1743 max: aabr.max.with_z(z.end),
1744 }
1745}
1746
1747#[expect(dead_code)]
1748pub fn aabb_corners<F: FnMut(Primitive) -> Id<Primitive>>(
1750 prim: &mut F,
1751 aabb: Aabb<i32>,
1752) -> Id<Primitive> {
1753 let f = |prim: &mut F, ret, vec| {
1754 let sub = prim(Primitive::Aabb(Aabb {
1755 min: aabb.min + vec,
1756 max: aabb.max - vec,
1757 }));
1758 prim(Primitive::Without(ret, sub))
1759 };
1760 let mut ret = prim(Primitive::Aabb(aabb));
1761 ret = f(prim, ret, Vec3::new(1, 0, 0));
1762 ret = f(prim, ret, Vec3::new(0, 1, 0));
1763 ret = f(prim, ret, Vec3::new(0, 0, 1));
1764 ret
1765}
1766
1767pub fn place_circular(
1768 center: Vec2<i32>,
1769 radius: f32,
1770 amount: i32,
1771) -> impl Iterator<Item = Vec2<i32>> {
1772 let phi = TAU / amount as f32;
1773 (1..=amount).map(move |n| {
1774 Vec2::new(
1775 center.x + (radius * ((n as f32 * phi).cos())) as i32,
1776 center.y + (radius * ((n as f32 * phi).sin())) as i32,
1777 )
1778 })
1779}
1780
1781pub fn place_circular_as_vec(center: Vec2<i32>, radius: f32, amount: i32) -> Vec<Vec2<i32>> {
1782 let phi = TAU / amount as f32;
1783 let mut positions = vec![];
1784 for n in 1..=amount {
1785 let pos = Vec2::new(
1786 center.x + (radius * ((n as f32 * phi).cos())) as i32,
1787 center.y + (radius * ((n as f32 * phi).sin())) as i32,
1788 );
1789 positions.push(pos);
1790 }
1791 positions
1792}
1793
1794pub fn spiral_staircase(
1795 origin: Vec3<i32>,
1796 radius: f32,
1797 inner_radius: f32,
1798 stretch: f32,
1799) -> Box<dyn Fn(Vec3<i32>) -> bool> {
1800 Box::new(move |pos: Vec3<i32>| {
1801 let pos = pos - origin;
1802 if (pos.xy().magnitude_squared() as f32) < inner_radius.powi(2) {
1803 true
1804 } else if (pos.xy().magnitude_squared() as f32) < radius.powi(2) {
1805 ((pos.x as f32).atan2(pos.y as f32) / (PI * 2.0) * stretch + pos.z as f32)
1806 .rem_euclid(stretch)
1807 < 1.5
1808 } else {
1809 false
1810 }
1811 })
1812}
1813
1814pub fn wall_staircase(
1815 origin: Vec3<i32>,
1816 radius: f32,
1817 stretch: f32,
1818) -> Box<dyn Fn(Vec3<i32>) -> bool> {
1819 Box::new(move |pos: Vec3<i32>| {
1820 let pos = pos - origin;
1821 if (pos.x.abs().max(pos.y.abs())) as f32 > 0.6 * radius {
1822 ((pos.x as f32).atan2(pos.y as f32) / (PI * 2.0) * stretch + pos.z as f32)
1823 .rem_euclid(stretch)
1824 < 1.0
1825 } else {
1826 false
1827 }
1828 })
1829}
1830
1831pub fn inscribed_polystar(
1832 origin: Vec2<i32>,
1833 radius: f32,
1834 sides: usize,
1835) -> Box<dyn Fn(Vec3<i32>) -> bool> {
1836 Box::new(move |pos| {
1837 use std::f32::consts::TAU;
1838 let rpos: Vec2<f32> = pos.xy().as_() - origin.as_();
1839 let is_border = rpos.magnitude_squared() > (radius - 2.0).powi(2);
1840 let is_line = (0..sides).any(|i| {
1841 let f = |j: f32| {
1842 let t = j * TAU / sides as f32;
1843 radius * Vec2::new(t.cos(), t.sin())
1844 };
1845 let line = LineSegment2 {
1846 start: f(i as f32),
1847 end: f((i + 2) as f32),
1848 };
1849 line.distance_to_point(rpos) <= 1.0
1850 });
1851 is_border || is_line
1852 })
1853}