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