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(
513 &self,
514 tree: &Store<Primitive>,
515 prim: Id<Primitive>,
516 pos: Vec3<i32>,
517 canvas_info: &CanvasInfo,
518 old_block: Block,
519 sprite_cfg: &mut Option<SpriteCfg>,
520 col: &ColInfo,
521 ) -> Option<Block> {
522 if Self::contains_at(tree, prim, pos, col) {
523 match self {
524 Fill::Sprite(sprite) | Fill::ResourceSprite(sprite) => {
525 Some(if old_block.is_filled() {
526 *sprite
527 } else {
528 old_block.with_data_of(*sprite)
529 })
530 },
531 Fill::CfgSprite(sprite, cfg) => {
532 *sprite_cfg = Some(cfg.clone());
533 Some(if old_block.is_filled() {
534 *sprite
535 } else {
536 old_block.with_data_of(*sprite)
537 })
538 },
539 Fill::Block(block) => Some(*block),
540 Fill::Brick(bk, col, range) => {
541 let pos = (pos + Vec3::new(pos.z, pos.z, 0)) / Vec3::new(2, 2, 1);
542 Some(Block::new(
543 *bk,
544 *col + ((((pos.x ^ pos.y ^ pos.z) as u8).reverse_bits() as u16
545 * *range as u16)
546 >> 8) as u8,
547 ))
551 },
552 Fill::PlankWall(bk, col, range) => Some(Block::new(
553 *bk,
554 *col + (RandomField::new(13)
555 .get((pos + Vec3::new(pos.z, pos.z, 0) * 8) / Vec3::new(16, 16, 1))
556 % *range as u32) as u8,
557 )),
558 Fill::Gradient(gradient, bk) => Some(Block::new(*bk, gradient.sample(pos.as_()))),
559 Fill::Prefab(p, tr, seed) => p.get(pos - tr).ok().and_then(|sb| {
560 let col_sample = canvas_info.col(canvas_info.wpos)?;
561 block_from_structure(
562 canvas_info.index,
563 sb,
564 pos - tr,
565 p.get_bounds().center().xy(),
566 *seed,
567 col_sample,
568 Block::air,
569 canvas_info.calendar(),
570 &Vec2::new(Vec2::new(1, 0), Vec2::new(0, 1)),
571 )
572 .map(|(block, cfg)| {
573 *sprite_cfg = cfg;
574 block
575 })
576 }),
577 Fill::Sampling(f) => f(pos),
578 }
579 } else {
580 None
581 }
582 }
583
584 fn get_bounds_inner(tree: &Store<Primitive>, prim: Id<Primitive>) -> Vec<Aabb<i32>> {
585 fn or_zip_with<T, F: FnOnce(T, T) -> T>(a: Option<T>, b: Option<T>, f: F) -> Option<T> {
586 match (a, b) {
587 (Some(a), Some(b)) => Some(f(a, b)),
588 (Some(a), _) => Some(a),
589 (_, b) => b,
590 }
591 }
592
593 match &tree[prim] {
594 Primitive::Empty => vec![],
595 Primitive::Aabb(aabb) => vec![*aabb],
596 Primitive::Pyramid { aabb, .. } => vec![*aabb],
597 Primitive::Gable { aabb, .. } => vec![*aabb],
598 Primitive::Ramp { aabb, .. } => vec![*aabb],
599 Primitive::Cylinder(aabb) => vec![*aabb],
600 Primitive::Cone(aabb) => vec![*aabb],
601 Primitive::Sphere(aabb) => vec![*aabb],
602 Primitive::Superquadric { aabb, .. } => vec![*aabb],
603 Primitive::Plane(aabr, origin, gradient) => {
604 let half_size = aabr.half_size().reduce_max();
605 let longest_dist = ((aabr.center() - origin.xy()).map(|x| x.abs())
606 + half_size
607 + aabr.size().reduce_max() % 2)
608 .map(|x| x as f32);
609 let z = if gradient.x.signum() == gradient.y.signum() {
610 Vec2::new(0, longest_dist.dot(*gradient) as i32)
611 } else {
612 (longest_dist * gradient).as_()
613 };
614 let aabb = Aabb {
615 min: aabr.min.with_z(origin.z + z.reduce_min().min(0)),
616 max: aabr.max.with_z(origin.z + z.reduce_max().max(0)),
617 };
618 vec![aabb.made_valid()]
619 },
620 Primitive::Segment { segment, r0, r1 } => {
621 let aabb = Aabb {
622 min: segment.start,
623 max: segment.end,
624 }
625 .made_valid();
626 vec![Aabb {
627 min: (aabb.min - r0.max(*r1)).floor().as_(),
628 max: (aabb.max + r0.max(*r1)).ceil().as_(),
629 }]
630 },
631 Primitive::SegmentPrism {
632 segment,
633 radius,
634 height,
635 } => {
636 let aabb = Aabb {
637 min: segment.start,
638 max: segment.end,
639 }
640 .made_valid();
641 let min = {
642 let xy = (aabb.min.xy() - *radius).floor();
643 xy.with_z(aabb.min.z).as_()
644 };
645 let max = {
646 let xy = (aabb.max.xy() + *radius).ceil();
647 xy.with_z((aabb.max.z + *height).ceil()).as_()
648 };
649 vec![Aabb { min, max }]
650 },
651 Primitive::Sampling(a, _) | Primitive::ColSampling(a, _) => {
652 Self::get_bounds_inner(tree, *a)
653 },
654 Primitive::Prefab(p) => vec![p.get_bounds()],
655 Primitive::Intersect(a, b) => or_zip_with(
656 Self::get_bounds_opt(tree, *a),
657 Self::get_bounds_opt(tree, *b),
658 |a, b| a.intersection(b),
659 )
660 .into_iter()
661 .collect(),
662
663 Primitive::Union(a, b) => {
664 fn jaccard(x: Aabb<i32>, y: Aabb<i32>) -> f32 {
665 let s_intersection = x.intersection(y).size().as_::<f32>().magnitude();
666 let s_union = x.union(y).size().as_::<f32>().magnitude();
667 s_intersection / s_union
668 }
669 let mut inputs = Vec::new();
670 inputs.extend(Self::get_bounds_inner(tree, *a));
671 inputs.extend(Self::get_bounds_inner(tree, *b));
672 let mut results = Vec::new();
673 if let Some(aabb) = inputs.pop() {
674 results.push(aabb);
675 for a in &inputs {
676 let best = results
677 .iter()
678 .enumerate()
679 .max_by_key(|(_, b)| (jaccard(*a, **b) * 1000.0) as usize);
680 match best {
681 Some((i, b)) if jaccard(*a, *b) > 0.3 => {
682 let mut aabb = results.swap_remove(i);
683 aabb = aabb.union(*a);
684 results.push(aabb);
685 },
686 _ => results.push(*a),
687 }
688 }
689 results
690 } else {
691 results
692 }
693 },
694 Primitive::Without(a, _) => Self::get_bounds_inner(tree, *a),
695 Primitive::Translate(prim, vec) => Self::get_bounds_inner(tree, *prim)
696 .into_iter()
697 .map(|aabb| Aabb {
698 min: aabb.min.map2(*vec, i32::saturating_add),
699 max: aabb.max.map2(*vec, i32::saturating_add),
700 })
701 .collect(),
702 Primitive::Scale(prim, vec) => Self::get_bounds_inner(tree, *prim)
703 .into_iter()
704 .map(|aabb| {
705 let center = aabb.center();
706 Aabb {
707 min: center + ((aabb.min - center).as_::<f32>() * vec).as_::<i32>(),
708 max: center + ((aabb.max - center).as_::<f32>() * vec).as_::<i32>(),
709 }
710 })
711 .collect(),
712 Primitive::RotateAbout(prim, mat, vec) => Self::get_bounds_inner(tree, *prim)
713 .into_iter()
714 .map(|aabb| {
715 let mat = mat.as_::<f32>();
716 let vec = vec - 0.5;
718 let new_aabb = Aabb::<f32> {
719 min: vec + mat * (aabb.min.as_() - vec),
720 max: vec + mat * ((aabb.max - 1).as_() - vec),
723 }
724 .made_valid();
725 Aabb::<i32> {
726 min: new_aabb.min.as_(),
727 max: new_aabb.max.as_() + 1,
728 }
729 })
730 .collect(),
731 Primitive::Repeat(prim, offset, count) => {
732 if count == &0 {
733 vec![]
734 } else {
735 let count = count - 1;
736 Self::get_bounds_inner(tree, *prim)
737 .into_iter()
738 .map(|aabb| Aabb {
739 min: aabb
740 .min
741 .map2(aabb.min + offset * count as i32, |a, b| a.min(b)),
742 max: aabb
743 .max
744 .map2(aabb.max + offset * count as i32, |a, b| a.max(b)),
745 })
746 .collect()
747 }
748 },
749 }
750 }
751
752 pub fn get_bounds_disjoint(tree: &Store<Primitive>, prim: Id<Primitive>) -> Vec<Aabb<i32>> {
753 Self::get_bounds_inner(tree, prim)
754 }
755
756 pub fn get_bounds_opt(tree: &Store<Primitive>, prim: Id<Primitive>) -> Option<Aabb<i32>> {
757 Self::get_bounds_inner(tree, prim)
758 .into_iter()
759 .reduce(|a, b| a.union(b))
760 }
761
762 pub fn get_bounds(tree: &Store<Primitive>, prim: Id<Primitive>) -> Aabb<i32> {
763 Self::get_bounds_opt(tree, prim).unwrap_or_else(|| Aabb::new_empty(Vec3::zero()))
764 }
765}
766
767pub struct Painter {
768 prims: RefCell<Store<Primitive>>,
769 fills: RefCell<Vec<(Id<Primitive>, Fill)>>,
770 entities: RefCell<Vec<EntityInfo>>,
771 render_area: Aabr<i32>,
772}
773
774impl Painter {
775 pub fn depth(&self, prim: Id<Primitive>) -> usize {
777 fn aux(prims: &Store<Primitive>, prim: Id<Primitive>, prev_depth: usize) -> usize {
778 match prims[prim] {
779 Primitive::Empty
780 | Primitive::Aabb(_)
781 | Primitive::Pyramid { .. }
782 | Primitive::Ramp { .. }
783 | Primitive::Gable { .. }
784 | Primitive::Cylinder(_)
785 | Primitive::Cone(_)
786 | Primitive::Sphere(_)
787 | Primitive::Superquadric { .. }
788 | Primitive::Plane(_, _, _)
789 | Primitive::Segment { .. }
790 | Primitive::SegmentPrism { .. }
791 | Primitive::Prefab(_) => prev_depth,
792 Primitive::Sampling(a, _)
793 | Primitive::ColSampling(a, _)
794 | Primitive::Translate(a, _)
795 | Primitive::Scale(a, _)
796 | Primitive::RotateAbout(a, _, _)
797 | Primitive::Repeat(a, _, _) => aux(prims, a, 1 + prev_depth),
798
799 Primitive::Intersect(a, b) | Primitive::Union(a, b) | Primitive::Without(a, b) => {
800 aux(prims, a, 1 + prev_depth).max(aux(prims, b, 1 + prev_depth))
801 },
802 }
803 }
804 let prims = self.prims.borrow();
805 aux(&prims, prim, 0)
806 }
807
808 pub fn order_by_depth(
811 &self,
812 a: impl Into<Id<Primitive>>,
813 b: impl Into<Id<Primitive>>,
814 ) -> (Id<Primitive>, Id<Primitive>) {
815 let (a, b) = (a.into(), b.into());
816 if self.depth(a) < self.depth(b) {
817 (a, b)
818 } else {
819 (b, a)
820 }
821 }
822
823 pub fn aabb(&self, aabb: Aabb<i32>) -> PrimitiveRef {
826 self.prim(Primitive::Aabb(aabb.made_valid()))
827 }
828
829 pub fn sphere(&self, aabb: Aabb<i32>) -> PrimitiveRef {
831 self.prim(Primitive::Sphere(aabb.made_valid()))
832 }
833
834 pub fn sphere_with_radius(&self, origin: Vec3<i32>, radius: f32) -> PrimitiveRef {
837 let min = origin - Vec3::broadcast(radius.round() as i32);
838 let max = origin + Vec3::broadcast(radius.round() as i32);
839 self.prim(Primitive::Sphere(Aabb { min, max }))
840 }
841
842 pub fn sphere2(&self, aabb: Aabb<i32>) -> PrimitiveRef {
846 let aabb = aabb.made_valid();
847 let radius = aabb.size().w.min(aabb.size().h) / 2;
848 let aabb = Aabb {
849 min: aabb.center() - radius,
850 max: aabb.center() + radius,
851 };
852 let degree = 2.0;
853 self.prim(Primitive::Superquadric { aabb, degree })
854 }
855
856 pub fn ellipsoid(&self, aabb: Aabb<i32>) -> PrimitiveRef {
859 let aabb = aabb.made_valid();
860 let degree = 2.0;
861 self.prim(Primitive::Superquadric { aabb, degree })
862 }
863
864 pub fn superquadric(&self, aabb: Aabb<i32>, degree: f32) -> PrimitiveRef {
874 let aabb = aabb.made_valid();
875 self.prim(Primitive::Superquadric { aabb, degree })
876 }
877
878 pub fn rounded_aabb(&self, aabb: Aabb<i32>) -> PrimitiveRef {
881 let aabb = aabb.made_valid();
882 self.prim(Primitive::Superquadric { aabb, degree: 3.0 })
883 }
884
885 pub fn cylinder(&self, aabb: Aabb<i32>) -> PrimitiveRef {
888 self.prim(Primitive::Cylinder(aabb.made_valid()))
889 }
890
891 pub fn horizontal_cylinder(&self, aabb: Aabb<i32>, dir: Dir) -> PrimitiveRef {
894 let aabr = Aabr::from(aabb);
895 let length = dir.select(aabr.size());
896 let height = aabb.max.z - aabb.min.z;
897 let aabb = Aabb {
898 min: (aabr.min - dir.abs().to_vec2() * height).with_z(aabb.min.z),
899 max: (dir.abs().select_with(aabr.min, aabr.max)).with_z(aabb.min.z + length),
900 };
901 self.cylinder(aabb)
902 .rotate_about((-dir.abs()).from_z_mat3(), aabr.min.with_z(aabb.min.z))
903 }
904
905 pub fn cylinder_with_radius(
908 &self,
909 origin: Vec3<i32>,
910 radius: f32,
911 height: f32,
912 ) -> PrimitiveRef {
913 let min = origin - Vec2::broadcast(radius.round() as i32);
914 let max = origin + Vec2::broadcast(radius.round() as i32).with_z(height.round() as i32);
915 self.prim(Primitive::Cylinder(Aabb { min, max }))
916 }
917
918 pub fn cone(&self, aabb: Aabb<i32>) -> PrimitiveRef {
921 self.prim(Primitive::Cone(aabb.made_valid()))
922 }
923
924 pub fn cone_with_radius(&self, origin: Vec3<i32>, radius: f32, height: f32) -> PrimitiveRef {
927 let min = origin - Vec2::broadcast(radius.round() as i32);
928 let max = origin + Vec2::broadcast(radius.round() as i32).with_z(height.round() as i32);
929 self.prim(Primitive::Cone(Aabb { min, max }))
930 }
931
932 pub fn line(
935 &self,
936 a: Vec3<impl AsPrimitive<f32>>,
937 b: Vec3<impl AsPrimitive<f32>>,
938 radius: f32,
939 ) -> PrimitiveRef {
940 self.prim(Primitive::Segment {
941 segment: LineSegment3 {
942 start: a.as_(),
943 end: b.as_(),
944 },
945 r0: radius,
946 r1: radius,
947 })
948 }
949
950 pub fn line_two_radius(
953 &self,
954 a: Vec3<impl AsPrimitive<f32>>,
955 b: Vec3<impl AsPrimitive<f32>>,
956 r0: f32,
957 r1: f32,
958 ) -> PrimitiveRef {
959 self.prim(Primitive::Segment {
960 segment: LineSegment3 {
961 start: a.as_(),
962 end: b.as_(),
963 },
964 r0,
965 r1,
966 })
967 }
968
969 pub fn segment_prism(
975 &self,
976 a: Vec3<impl AsPrimitive<f32>>,
977 b: Vec3<impl AsPrimitive<f32>>,
978 radius: f32,
979 height: f32,
980 ) -> PrimitiveRef {
981 let segment = LineSegment3 {
982 start: a.as_(),
983 end: b.as_(),
984 };
985 self.prim(Primitive::SegmentPrism {
986 segment,
987 radius,
988 height,
989 })
990 }
991
992 pub fn cubic_bezier(
996 &self,
997 start: Vec3<impl AsPrimitive<f32>>,
998 ctrl0: Vec3<impl AsPrimitive<f32>>,
999 ctrl1: Vec3<impl AsPrimitive<f32>>,
1000 end: Vec3<impl AsPrimitive<f32>>,
1001 radius: f32,
1002 ) -> PrimitiveRef {
1003 let bezier = CubicBezier3 {
1004 start: start.as_(),
1005 ctrl0: ctrl0.as_(),
1006 ctrl1: ctrl1.as_(),
1007 end: end.as_(),
1008 };
1009 let length = bezier.length_by_discretization(10);
1010 let num_segments = (0.2 * length).ceil() as u16;
1011 self.cubic_bezier_with_num_segments(bezier, radius, num_segments)
1012 }
1013
1014 pub fn cubic_bezier_with_num_segments(
1017 &self,
1018 bezier: CubicBezier3<f32>,
1019 radius: f32,
1020 num_segments: u16,
1021 ) -> PrimitiveRef {
1022 let mut bezier_prim = self.empty();
1023 let range: Vec<_> = (0..=num_segments).collect();
1024 range.windows(2).for_each(|w| {
1025 let segment_start = bezier.evaluate(w[0] as f32 / num_segments as f32);
1026 let segment_end = bezier.evaluate(w[1] as f32 / num_segments as f32);
1027 bezier_prim = bezier_prim.union(self.line(segment_start, segment_end, radius));
1028 });
1029 bezier_prim
1030 }
1031
1032 pub fn cubic_bezier_prism(
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 height: f32,
1046 ) -> PrimitiveRef {
1047 let bezier = CubicBezier3 {
1048 start: start.as_(),
1049 ctrl0: ctrl0.as_(),
1050 ctrl1: ctrl1.as_(),
1051 end: end.as_(),
1052 };
1053 let length = bezier.length_by_discretization(10);
1054 let num_segments = (0.2 * length).ceil() as u16;
1055 self.cubic_bezier_prism_with_num_segments(bezier, radius, height, num_segments)
1056 }
1057
1058 pub fn cubic_bezier_prism_with_num_segments(
1064 &self,
1065 bezier: CubicBezier3<f32>,
1066 radius: f32,
1067 height: f32,
1068 num_segments: u16,
1069 ) -> PrimitiveRef {
1070 let mut bezier_prim = self.empty();
1071 let range: Vec<_> = (0..=num_segments).collect();
1072 range.windows(2).for_each(|w| {
1073 let segment_start = bezier.evaluate(w[0] as f32 / num_segments as f32);
1074 let segment_end = bezier.evaluate(w[1] as f32 / num_segments as f32);
1075 bezier_prim =
1076 bezier_prim.union(self.segment_prism(segment_start, segment_end, radius, height));
1077 });
1078 bezier_prim
1079 }
1080
1081 pub fn plane(&self, aabr: Aabr<i32>, origin: Vec3<i32>, gradient: Vec2<f32>) -> PrimitiveRef {
1086 let aabr = aabr.made_valid();
1087 self.prim(Primitive::Plane(aabr, origin, gradient))
1088 }
1089
1090 pub fn ramp_inset(&self, aabb: Aabb<i32>, inset: i32, dir: Dir) -> PrimitiveRef {
1094 let aabb = aabb.made_valid();
1095 self.prim(Primitive::Ramp { aabb, inset, dir })
1096 }
1097
1098 pub fn ramp(&self, aabb: Aabb<i32>, dir: Dir) -> PrimitiveRef {
1099 let aabb = aabb.made_valid();
1100 self.prim(Primitive::Ramp {
1101 aabb,
1102 inset: dir.select((aabb.size().w, aabb.size().h)),
1103 dir,
1104 })
1105 }
1106
1107 pub fn gable(&self, aabb: Aabb<i32>, inset: i32, dir: Dir) -> PrimitiveRef {
1111 let aabb = aabb.made_valid();
1112 self.prim(Primitive::Gable { aabb, inset, dir })
1113 }
1114
1115 pub fn sprite(&self, pos: Vec3<i32>, sprite: SpriteKind) {
1117 self.aabb(Aabb {
1118 min: pos,
1119 max: pos + 1,
1120 })
1121 .fill(Fill::sprite(sprite))
1122 }
1123
1124 pub fn rotated_sprite(&self, pos: Vec3<i32>, sprite: SpriteKind, ori: u8) {
1126 self.aabb(Aabb {
1127 min: pos,
1128 max: pos + 1,
1129 })
1130 .fill(Fill::sprite_ori(sprite, ori % 8))
1131 }
1132
1133 pub fn rotated_sprite_with_cfg(
1136 &self,
1137 pos: Vec3<i32>,
1138 sprite: SpriteKind,
1139 ori: u8,
1140 cfg: SpriteCfg,
1141 ) {
1142 self.aabb(Aabb {
1143 min: pos,
1144 max: pos + 1,
1145 })
1146 .fill(Fill::sprite_ori_cfg(sprite, ori % 8, cfg))
1147 }
1148
1149 pub fn resource_sprite(&self, pos: Vec3<i32>, sprite: SpriteKind, ori: u8) {
1153 self.aabb(Aabb {
1154 min: pos,
1155 max: pos + 1,
1156 })
1157 .fill(Fill::resource_sprite_ori(sprite, ori % 8))
1158 }
1159
1160 pub fn owned_resource_sprite(&self, pos: Vec3<i32>, sprite: SpriteKind, ori: u8) {
1164 self.aabb(Aabb {
1165 min: pos,
1166 max: pos + 1,
1167 })
1168 .fill(Fill::owned_resource_sprite_ori(sprite, ori))
1169 }
1170
1171 pub fn pyramid(&self, aabb: Aabb<i32>) -> PrimitiveRef {
1174 let inset = 0;
1175 let aabb = aabb.made_valid();
1176 self.prim(Primitive::Ramp {
1177 aabb,
1178 inset,
1179 dir: Dir::X,
1180 })
1181 .intersect(self.prim(Primitive::Ramp {
1182 aabb,
1183 inset,
1184 dir: Dir::NegX,
1185 }))
1186 .intersect(self.prim(Primitive::Ramp {
1187 aabb,
1188 inset,
1189 dir: Dir::Y,
1190 }))
1191 .intersect(self.prim(Primitive::Ramp {
1192 aabb,
1193 inset,
1194 dir: Dir::NegY,
1195 }))
1196 }
1197
1198 pub fn prim(&self, prim: Primitive) -> PrimitiveRef {
1201 PrimitiveRef {
1202 id: self.prims.borrow_mut().insert(prim),
1203 painter: self,
1204 }
1205 }
1206
1207 pub fn empty(&self) -> PrimitiveRef { self.prim(Primitive::Empty) }
1210
1211 pub fn fill(&self, prim: impl Into<Id<Primitive>>, fill: Fill) {
1213 let prim = prim.into();
1214 if let Primitive::Union(a, b) = self.prims.borrow()[prim] {
1215 self.fill(a, fill.clone());
1216 self.fill(b, fill);
1217 } else {
1218 self.fills.borrow_mut().push((prim, fill));
1219 }
1220 }
1221
1222 pub fn vault(&self, aabb: Aabb<i32>, dir: Dir) -> PrimitiveRef {
1232 let h = dir.orthogonal().select(Vec3::from(aabb.size()).xy());
1233
1234 let mut prim = self.horizontal_cylinder(
1235 Aabb {
1236 min: aabb.min.with_z(aabb.max.z - h),
1237 max: aabb.max,
1238 },
1239 dir,
1240 );
1241
1242 if aabb.size().d < h {
1243 prim = prim.intersect(self.aabb(aabb));
1244 }
1245
1246 self.aabb(Aabb {
1247 min: aabb.min,
1248 max: aabb.max.with_z(aabb.max.z - h / 2),
1249 })
1250 .union(prim)
1251 }
1252
1253 pub fn aabbs_around_aabb(&self, aabb: Aabb<i32>, size: i32, offset: i32) -> PrimitiveRef {
1255 let pillar = self.aabb(Aabb {
1256 min: (aabb.min.xy() - 1).with_z(aabb.min.z),
1257 max: (aabb.min.xy() + size - 1).with_z(aabb.max.z),
1258 });
1259
1260 let true_offset = offset + size;
1261
1262 let size_x = aabb.max.x - aabb.min.x;
1263 let size_y = aabb.max.y - aabb.min.y;
1264
1265 let num_aabbs = ((size_x + 1) / 2) / true_offset;
1266 let x = pillar.repeat(Vec3::new(true_offset, 0, 0), num_aabbs as u32 + 1);
1267
1268 let num_aabbs = ((size_y + 1) / 2) / true_offset;
1269 let y = pillar.repeat(Vec3::new(0, true_offset, 0), num_aabbs as u32 + 1);
1270 let center = aabb.as_::<f32>().center();
1271 let shape = x.union(y);
1272 let shape =
1273 shape.union(shape.rotate_about(Mat3::new(-1, 0, 0, 0, -1, 0, 0, 0, -1), center));
1274 shape.union(shape.rotate_about(Mat3::new(0, 1, 0, -1, 0, 0, 0, 0, 1), center))
1275 }
1276
1277 pub fn staircase_in_aabb(
1278 &self,
1279 aabb: Aabb<i32>,
1280 thickness: i32,
1281 start_dir: Dir,
1282 ) -> PrimitiveRef {
1283 let mut forward = start_dir;
1284 let mut z = aabb.max.z - 1;
1285 let aabr = Aabr::from(aabb);
1286
1287 let mut prim = self.empty();
1288
1289 while z > aabb.min.z {
1290 let right = forward.rotated_cw();
1291 let fc = forward.select_aabr(aabr);
1292 let corner =
1293 fc * forward.abs().to_vec2() + right.select_aabr(aabr) * right.abs().to_vec2();
1294 let aabb = Aabb {
1295 min: corner.with_z(z),
1296 max: (corner - (forward.to_vec2() + right.to_vec2()) * thickness).with_z(z + 1),
1297 }
1298 .made_valid();
1299
1300 let stair_len = ((fc - (-forward).select_aabr(aabr)).abs() - thickness * 2).max(1) + 1;
1301
1302 let stairs = self.ramp(
1303 Aabb {
1304 min: (corner - right.to_vec2() * (stair_len + thickness)).with_z(z - stair_len),
1305 max: (corner
1306 - right.to_vec2() * (thickness - 1)
1307 - forward.to_vec2() * thickness)
1308 .with_z(z + 1),
1309 }
1310 .made_valid(),
1311 right,
1312 );
1313
1314 prim = prim
1315 .union(self.aabb(aabb))
1316 .union(stairs.without(stairs.translate(Vec3::new(0, 0, -2))));
1317
1318 z -= stair_len;
1319 forward = forward.rotated_ccw();
1320 }
1321 prim
1322 }
1323
1324 pub fn numeral(&self, origin: Vec3<i32>, numeral: usize) -> PrimitiveRef {
1326 let bottom_bar = self.aabb(Aabb {
1327 min: Vec2::new(origin.x, origin.y).with_z(origin.z),
1328 max: Vec2::new(origin.x + 1, origin.y + 4).with_z(origin.z + 1),
1329 });
1330 let mid_bar = self.aabb(Aabb {
1331 min: Vec2::new(origin.x, origin.y).with_z(origin.z + 2),
1332 max: Vec2::new(origin.x + 1, origin.y + 4).with_z(origin.z + 3),
1333 });
1334 let top_bar = self.aabb(Aabb {
1335 min: Vec2::new(origin.x, origin.y).with_z(origin.z + 4),
1336 max: Vec2::new(origin.x + 1, origin.y + 4).with_z(origin.z + 5),
1337 });
1338 let left_top = self.aabb(Aabb {
1339 min: Vec2::new(origin.x, origin.y).with_z(origin.z + 2),
1340 max: Vec2::new(origin.x + 1, origin.y + 1).with_z(origin.z + 5),
1341 });
1342 let left_bottom = self.aabb(Aabb {
1343 min: Vec2::new(origin.x, origin.y).with_z(origin.z),
1344 max: Vec2::new(origin.x + 1, origin.y + 1).with_z(origin.z + 3),
1345 });
1346 let right_top = self.aabb(Aabb {
1347 min: Vec2::new(origin.x, origin.y + 3).with_z(origin.z + 2),
1348 max: Vec2::new(origin.x + 1, origin.y + 4).with_z(origin.z + 5),
1349 });
1350 let right_bottom = self.aabb(Aabb {
1351 min: Vec2::new(origin.x, origin.y + 3).with_z(origin.z),
1352 max: Vec2::new(origin.x + 1, origin.y + 4).with_z(origin.z + 3),
1353 });
1354 let number_strokes = match numeral {
1355 0 => &[
1356 top_bar,
1357 bottom_bar,
1358 right_top,
1359 right_bottom,
1360 left_top,
1361 left_bottom,
1362 ] as &[_],
1363 1 => &[right_top, right_bottom],
1364 2 => &[top_bar, right_top, mid_bar, left_bottom, bottom_bar],
1365 3 => &[top_bar, bottom_bar, mid_bar, right_top, right_bottom],
1366 4 => &[left_top, mid_bar, right_top, right_bottom],
1367 5 => &[top_bar, left_top, mid_bar, right_bottom, bottom_bar],
1368 6 => &[
1369 top_bar,
1370 left_top,
1371 left_bottom,
1372 bottom_bar,
1373 right_bottom,
1374 mid_bar,
1375 ],
1376 7 => &[top_bar, right_top, right_bottom],
1377 8 => &[
1378 top_bar,
1379 left_top,
1380 left_bottom,
1381 mid_bar,
1382 right_top,
1383 right_bottom,
1384 bottom_bar,
1385 ],
1386 _ => &[top_bar, left_top, mid_bar, right_top, right_bottom],
1387 };
1388
1389 let mut prim = self.empty();
1390 for stroke in number_strokes {
1391 prim = prim.union(*stroke)
1392 }
1393
1394 prim
1395 }
1396
1397 pub fn column(&self, point: Vec2<i32>, range: impl RangeBounds<i32>) -> PrimitiveRef {
1398 self.aabb(Aabb {
1399 min: point.with_z(match range.start_bound() {
1400 std::ops::Bound::Included(n) => *n,
1401 std::ops::Bound::Excluded(n) => n + 1,
1402 std::ops::Bound::Unbounded => i32::MIN,
1403 }),
1404 max: (point + 1).with_z(match range.end_bound() {
1405 std::ops::Bound::Included(n) => n + 1,
1406 std::ops::Bound::Excluded(n) => *n,
1407 std::ops::Bound::Unbounded => i32::MAX,
1408 }),
1409 })
1410 }
1411
1412 pub fn render_aabr(&self) -> Aabr<i32> { self.render_area }
1414
1415 pub fn spawn(&self, entity: EntityInfo) {
1417 if self.render_area.contains_point(entity.pos.xy().as_()) {
1418 self.entities.borrow_mut().push(entity)
1419 }
1420 }
1421}
1422
1423pub fn render_prefab(file_path: &str, position: Vec3<i32>, painter: &Painter) {
1424 let asset_handle = PrefabStructure::load_group(file_path);
1425 let prefab_structure = asset_handle.read()[0].clone();
1426
1427 painter
1429 .prim(Primitive::Prefab(Box::new(prefab_structure.clone())))
1430 .translate(position)
1431 .fill(Fill::Prefab(Box::new(prefab_structure), position, 0));
1432}
1433
1434#[derive(Copy, Clone)]
1435pub struct PrimitiveRef<'a> {
1436 id: Id<Primitive>,
1437 painter: &'a Painter,
1438}
1439
1440impl<'a> From<PrimitiveRef<'a>> for Id<Primitive> {
1441 fn from(r: PrimitiveRef<'a>) -> Self { r.id }
1442}
1443
1444impl<'a> PrimitiveRef<'a> {
1445 #[must_use]
1448 pub fn union(self, other: impl Into<Id<Primitive>>) -> PrimitiveRef<'a> {
1449 let (a, b) = self.painter.order_by_depth(self, other);
1450 self.painter.prim(Primitive::union(a, b))
1451 }
1452
1453 #[must_use]
1456 pub fn intersect(self, other: impl Into<Id<Primitive>>) -> PrimitiveRef<'a> {
1457 let (a, b) = self.painter.order_by_depth(self, other);
1458 self.painter.prim(Primitive::intersect(a, b))
1459 }
1460
1461 #[must_use]
1464 pub fn without(self, other: impl Into<Id<Primitive>>) -> PrimitiveRef<'a> {
1465 self.painter.prim(Primitive::without(self, other))
1466 }
1467
1468 pub fn fill(self, fill: Fill) { self.painter.fill(self, fill); }
1470
1471 pub fn clear(self) { self.painter.fill(self, Fill::Block(Block::empty())); }
1475
1476 #[must_use]
1479 pub fn sample(self, sampling: impl Fn(Vec3<i32>) -> bool + 'static) -> PrimitiveRef<'a> {
1480 self.painter
1481 .prim(Primitive::sampling(self, Box::new(sampling)))
1482 }
1483
1484 #[must_use]
1487 pub fn sample_with_column(
1488 self,
1489 sampling: impl Fn(Vec3<i32>, &ColInfo) -> bool + 'static,
1490 ) -> PrimitiveRef<'a> {
1491 self.painter
1492 .prim(Primitive::column_sampling(self, Box::new(sampling)))
1493 }
1494
1495 #[must_use]
1497 pub fn rotate_about_min(self, mat: Mat3<i32>) -> PrimitiveRef<'a> {
1498 let point = Fill::get_bounds(&self.painter.prims.borrow(), self.into()).min;
1499 self.rotate_about(mat, point)
1500 }
1501}
1502
1503pub trait PrimitiveTransform {
1505 #[must_use]
1507 fn translate(self, trans: Vec3<i32>) -> Self;
1508 #[must_use]
1511 fn rotate_about(self, rot: Mat3<i32>, point: Vec3<impl AsPrimitive<f32>>) -> Self;
1512 #[must_use]
1515 fn rotate_z_90_about(self, n: i32, point: Vec3<impl AsPrimitive<f32>>) -> Self;
1516 #[must_use]
1519 fn scale(self, scale: Vec3<impl AsPrimitive<f32>>) -> Self;
1520 #[must_use]
1524 fn repeat(self, offset: Vec3<i32>, count: u32) -> Self;
1525}
1526
1527impl PrimitiveTransform for PrimitiveRef<'_> {
1528 fn translate(self, trans: Vec3<i32>) -> Self {
1529 self.painter.prim(Primitive::translate(self, trans))
1530 }
1531
1532 fn rotate_about(self, rot: Mat3<i32>, point: Vec3<impl AsPrimitive<f32>>) -> Self {
1533 self.painter.prim(Primitive::rotate_about(self, rot, point))
1534 }
1535
1536 fn rotate_z_90_about(self, n: i32, point: Vec3<impl AsPrimitive<f32>>) -> Self {
1537 self.painter
1538 .prim(Primitive::rotate_z_90_about(self, n, point))
1539 }
1540
1541 fn scale(self, scale: Vec3<impl AsPrimitive<f32>>) -> Self {
1542 self.painter.prim(Primitive::scale(self, scale.as_()))
1543 }
1544
1545 fn repeat(self, offset: Vec3<i32>, count: u32) -> Self {
1546 self.painter.prim(Primitive::repeat(self, offset, count))
1547 }
1548}
1549
1550impl<const N: usize> PrimitiveTransform for [PrimitiveRef<'_>; N] {
1551 fn translate(mut self, trans: Vec3<i32>) -> Self {
1552 for prim in &mut self {
1553 *prim = prim.translate(trans);
1554 }
1555 self
1556 }
1557
1558 fn rotate_about(mut self, rot: Mat3<i32>, point: Vec3<impl AsPrimitive<f32>>) -> Self {
1559 for prim in &mut self {
1560 *prim = prim.rotate_about(rot, point);
1561 }
1562 self
1563 }
1564
1565 fn rotate_z_90_about(mut self, n: i32, point: Vec3<impl AsPrimitive<f32>>) -> Self {
1566 for prim in &mut self {
1567 *prim = prim.rotate_z_90_about(n, point);
1568 }
1569 self
1570 }
1571
1572 fn scale(mut self, scale: Vec3<impl AsPrimitive<f32>>) -> Self {
1573 for prim in &mut self {
1574 *prim = prim.scale(scale);
1575 }
1576 self
1577 }
1578
1579 fn repeat(mut self, offset: Vec3<i32>, count: u32) -> Self {
1580 for prim in &mut self {
1581 *prim = prim.repeat(offset, count);
1582 }
1583 self
1584 }
1585}
1586
1587pub trait Structure {
1588 #[cfg(feature = "use-dyn-lib")]
1589 const UPDATE_FN: &'static [u8];
1590
1591 fn render_inner(&self, _site: &Site, _land: &Land, _painter: &Painter) {}
1592
1593 fn render(&self, site: &Site, land: &Land, painter: &Painter) {
1594 #[cfg(not(feature = "use-dyn-lib"))]
1595 {
1596 self.render_inner(site, land, painter);
1597 }
1598 #[cfg(feature = "use-dyn-lib")]
1599 {
1600 let lock = LIB.lock().unwrap();
1601 let lib = &lock.as_ref().unwrap().lib;
1602
1603 let update_fn: common_dynlib::Symbol<fn(&Self, &Site, &Land, &Painter)> = unsafe {
1604 lib.get(Self::UPDATE_FN)
1607 }
1609 .unwrap_or_else(|e| {
1610 panic!(
1611 "Trying to use: {} but had error: {:?}",
1612 CStr::from_bytes_with_nul(Self::UPDATE_FN)
1613 .map(CStr::to_str)
1614 .unwrap()
1615 .unwrap(),
1616 e
1617 )
1618 });
1619
1620 update_fn(self, site, land, painter);
1621 }
1622 }
1623
1624 fn render_collect(
1626 &self,
1627 site: &Site,
1628 canvas: &CanvasInfo,
1629 ) -> (
1630 Store<Primitive>,
1631 Vec<(Id<Primitive>, Fill)>,
1632 Vec<EntityInfo>,
1633 ) {
1634 let painter = Painter {
1635 prims: RefCell::new(Store::default()),
1636 fills: RefCell::new(Vec::new()),
1637 entities: RefCell::new(Vec::new()),
1638 render_area: Aabr {
1639 min: canvas.wpos,
1640 max: canvas.wpos + TerrainChunkSize::RECT_SIZE.map(|e| e as i32),
1641 },
1642 };
1643
1644 self.render(site, &canvas.land(), &painter);
1645 (
1646 painter.prims.into_inner(),
1647 painter.fills.into_inner(),
1648 painter.entities.into_inner(),
1649 )
1650 }
1651
1652 fn rel_terrain_offset(&self, col: &ColumnSample) -> i32 { col.alt as i32 }
1655
1656 fn terrain_surface_at<R: Rng>(
1657 &self,
1658 _wpos: Vec2<i32>,
1659 _old: Block,
1660 _rng: &mut R,
1661 _col: &ColumnSample,
1662 _z_off: i32,
1663 _site: &Site,
1664 ) -> Option<Block> {
1665 None
1666 }
1667}
1668
1669pub fn aabr_with_z<T>(aabr: Aabr<T>, z: Range<T>) -> Aabb<T> {
1671 Aabb {
1672 min: aabr.min.with_z(z.start),
1673 max: aabr.max.with_z(z.end),
1674 }
1675}
1676
1677#[expect(dead_code)]
1678pub fn aabb_corners<F: FnMut(Primitive) -> Id<Primitive>>(
1680 prim: &mut F,
1681 aabb: Aabb<i32>,
1682) -> Id<Primitive> {
1683 let f = |prim: &mut F, ret, vec| {
1684 let sub = prim(Primitive::Aabb(Aabb {
1685 min: aabb.min + vec,
1686 max: aabb.max - vec,
1687 }));
1688 prim(Primitive::Without(ret, sub))
1689 };
1690 let mut ret = prim(Primitive::Aabb(aabb));
1691 ret = f(prim, ret, Vec3::new(1, 0, 0));
1692 ret = f(prim, ret, Vec3::new(0, 1, 0));
1693 ret = f(prim, ret, Vec3::new(0, 0, 1));
1694 ret
1695}
1696
1697pub fn place_circular(
1698 center: Vec2<i32>,
1699 radius: f32,
1700 amount: i32,
1701) -> impl Iterator<Item = Vec2<i32>> {
1702 let phi = TAU / amount as f32;
1703 (1..=amount).map(move |n| {
1704 Vec2::new(
1705 center.x + (radius * ((n as f32 * phi).cos())) as i32,
1706 center.y + (radius * ((n as f32 * phi).sin())) as i32,
1707 )
1708 })
1709}
1710
1711pub fn place_circular_as_vec(center: Vec2<i32>, radius: f32, amount: i32) -> Vec<Vec2<i32>> {
1712 let phi = TAU / amount as f32;
1713 let mut positions = vec![];
1714 for n in 1..=amount {
1715 let pos = Vec2::new(
1716 center.x + (radius * ((n as f32 * phi).cos())) as i32,
1717 center.y + (radius * ((n as f32 * phi).sin())) as i32,
1718 );
1719 positions.push(pos);
1720 }
1721 positions
1722}
1723
1724pub fn spiral_staircase(
1725 origin: Vec3<i32>,
1726 radius: f32,
1727 inner_radius: f32,
1728 stretch: f32,
1729) -> Box<dyn Fn(Vec3<i32>) -> bool> {
1730 Box::new(move |pos: Vec3<i32>| {
1731 let pos = pos - origin;
1732 if (pos.xy().magnitude_squared() as f32) < inner_radius.powi(2) {
1733 true
1734 } else if (pos.xy().magnitude_squared() as f32) < radius.powi(2) {
1735 ((pos.x as f32).atan2(pos.y as f32) / (PI * 2.0) * stretch + pos.z as f32)
1736 .rem_euclid(stretch)
1737 < 1.5
1738 } else {
1739 false
1740 }
1741 })
1742}
1743
1744pub fn wall_staircase(
1745 origin: Vec3<i32>,
1746 radius: f32,
1747 stretch: f32,
1748) -> Box<dyn Fn(Vec3<i32>) -> bool> {
1749 Box::new(move |pos: Vec3<i32>| {
1750 let pos = pos - origin;
1751 if (pos.x.abs().max(pos.y.abs())) as f32 > 0.6 * radius {
1752 ((pos.x as f32).atan2(pos.y as f32) / (PI * 2.0) * stretch + pos.z as f32)
1753 .rem_euclid(stretch)
1754 < 1.0
1755 } else {
1756 false
1757 }
1758 })
1759}
1760
1761pub fn inscribed_polystar(
1762 origin: Vec2<i32>,
1763 radius: f32,
1764 sides: usize,
1765) -> Box<dyn Fn(Vec3<i32>) -> bool> {
1766 Box::new(move |pos| {
1767 use std::f32::consts::TAU;
1768 let rpos: Vec2<f32> = pos.xy().as_() - origin.as_();
1769 let is_border = rpos.magnitude_squared() > (radius - 2.0).powi(2);
1770 let is_line = (0..sides).any(|i| {
1771 let f = |j: f32| {
1772 let t = j * TAU / sides as f32;
1773 radius * Vec2::new(t.cos(), t.sin())
1774 };
1775 let line = LineSegment2 {
1776 start: f(i as f32),
1777 end: f((i + 2) as f32),
1778 };
1779 line.distance_to_point(rpos) <= 1.0
1780 });
1781 is_border || is_line
1782 })
1783}