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