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