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