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