1use bytemuck::{Pod, Zeroable};
2use loam_math::hyperbolic::{
3 klein_to_poincare, poincare_to_klein, H3_DEPTH_ENVELOPE, H3_EYE_CHART_REACH,
4};
5use loam_math::{EuclideanR3, EuclideanR4, HyperbolicH3, Iso3, IsometryGroup, Space};
6use loam_shape::polytope::SectionScratch;
7use loam_shape::{LineMesh, TriangleMesh};
8use std::fmt;
9use std::ops::{Deref, DerefMut, Range};
10use std::sync::Arc;
11
12use crate::domain::{ChartPoint, ChartPose, DomainError, DomainId, DomainSpace, Pose};
13use crate::entity::Entity;
14use crate::session::{MaterialId, PreparedId, Stamp};
15use crate::store::{Cursor, RecordBuffer};
16
17pub(crate) type Vec3 = <EuclideanR3 as Space>::Point;
18pub(crate) type Vec4 = <EuclideanR4 as Space>::Point;
19
20#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
21pub struct ViewId(u32);
22
23impl ViewId {
24 pub(crate) fn new(index: usize) -> Self {
25 Self(index as u32)
26 }
27
28 pub fn index(self) -> usize {
29 self.0 as usize
30 }
31}
32
33impl fmt::Display for ViewId {
34 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35 write!(f, "view {}", self.0)
36 }
37}
38
39#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
41pub struct ImageSpaceId {
42 slot: u32,
43 generation: u32,
44}
45
46impl ImageSpaceId {
47 pub fn index(self) -> usize {
48 self.slot as usize
49 }
50}
51
52impl fmt::Display for ImageSpaceId {
53 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54 write!(f, "image space {} gen {}", self.slot, self.generation)
55 }
56}
57
58#[derive(Clone, Copy, Debug, PartialEq, Eq)]
59pub struct ViewTarget {
60 pub view: ViewId,
61 pub image: ImageSpaceId,
62}
63
64#[derive(Clone, Copy, Debug, PartialEq)]
66pub struct Eye {
67 pub position: [f32; 3],
68 pub right: [f32; 3],
69 pub up: [f32; 3],
70 pub forward: [f32; 3],
71 pub fov_y: f32,
72 pub aspect: f32,
73 pub near: f32,
74 pub far: f32,
75}
76
77impl Eye {
78 pub fn looking_at(position: [f32; 3], target: [f32; 3], up: [f32; 3]) -> Self {
79 let forward = (Vec3::from(target) - Vec3::from(position))
80 .try_normalize()
81 .unwrap_or(-Vec3::Z);
82 let right = forward
83 .cross(Vec3::from(up))
84 .try_normalize()
85 .unwrap_or(Vec3::X);
86 Self {
87 position,
88 right: right.to_array(),
89 up: right.cross(forward).to_array(),
90 forward: forward.to_array(),
91 ..Self::default()
92 }
93 }
94
95 fn forward_distance(&self, point: [f32; 3]) -> (Vec3, f32) {
96 let relative = Vec3::from(point) - Vec3::from(self.position);
97 (relative, relative.dot(Vec3::from(self.forward)))
98 }
99}
100
101impl Default for Eye {
102 fn default() -> Self {
103 Self {
104 position: [0.0; 3],
105 right: [1.0, 0.0, 0.0],
106 up: [0.0, 1.0, 0.0],
107 forward: [0.0, 0.0, -1.0],
108 fov_y: 60.0_f32.to_radians(),
109 aspect: 1.0,
110 near: 0.05,
111 far: 100.0,
112 }
113 }
114}
115
116pub struct ImageSpace {
117 pub eye: Eye,
118}
119
120#[derive(Clone, Copy, Debug, PartialEq)]
121pub struct Rigid {
122 pub pose: Iso3,
123 pub scale: f32,
124}
125
126impl Rigid {
127 pub const IDENTITY: Self = Self {
128 pose: Iso3::IDENTITY,
129 scale: 1.0,
130 };
131
132 pub fn apply(&self, point: [f32; 3]) -> [f32; 3] {
133 EuclideanR3
134 .iso_apply(self.pose, Vec3::from(point) * self.scale)
135 .to_array()
136 }
137
138 pub fn compose(&self, inner: &Rigid) -> Rigid {
140 Rigid {
141 pose: EuclideanR3.iso_compose(
142 self.pose,
143 Iso3 {
144 rotation: inner.pose.rotation,
145 translation: inner.pose.translation * self.scale,
146 },
147 ),
148 scale: self.scale * inner.scale,
149 }
150 }
151
152 pub fn inverse(&self) -> Rigid {
153 let inverse = EuclideanR3.iso_inverse(self.pose);
154 Rigid {
155 pose: Iso3 {
156 rotation: inverse.rotation,
157 translation: inverse.translation / self.scale,
158 },
159 scale: 1.0 / self.scale,
160 }
161 }
162
163 pub fn direction(&self, direction: [f32; 3]) -> [f32; 3] {
164 EuclideanR3
165 .iso_transport(self.pose, Vec3::ZERO, Vec3::from(direction))
166 .to_array()
167 }
168
169 pub fn ray(&self, ray: &ImageRay) -> ImageRay {
170 ImageRay {
171 origin: self.apply(ray.origin),
172 direction: self.direction(ray.direction),
173 }
174 }
175}
176
177#[derive(Clone, Copy, Debug, PartialEq)]
178pub enum Placement {
179 Rigid(Rigid),
180 Nonlinear(&'static str),
181}
182
183impl Placement {
184 pub fn compose(&self, inner: &Placement) -> Placement {
185 match (self, inner) {
186 (Placement::Rigid(outer), Placement::Rigid(inner)) => {
187 Placement::Rigid(outer.compose(inner))
188 }
189 (Placement::Nonlinear(kind), _) | (_, Placement::Nonlinear(kind)) => {
190 Placement::Nonlinear(kind)
191 }
192 }
193 }
194
195 pub fn rigid(&self) -> Option<Rigid> {
196 match self {
197 Placement::Rigid(rigid) => Some(*rigid),
198 Placement::Nonlinear(_) => None,
199 }
200 }
201}
202
203#[derive(Clone, Copy)]
204struct Placed {
205 parent: ImageSpaceId,
206 placement: Placement,
207 generation: u32,
208 live: bool,
209}
210
211#[derive(Clone, Default)]
212pub struct ViewsSnapshot {
213 root_eye: Eye,
214 placed: Vec<Placed>,
215}
216
217pub struct Views {
218 root: ImageSpace,
219 placed: Vec<Placed>,
220}
221
222impl Views {
223 pub(crate) fn new() -> Self {
224 Self {
225 root: ImageSpace {
226 eye: Eye::default(),
227 },
228 placed: Vec::new(),
229 }
230 }
231
232 pub fn root(&self) -> ImageSpaceId {
233 ImageSpaceId {
234 slot: 0,
235 generation: 0,
236 }
237 }
238
239 pub fn root_mut(&mut self) -> &mut ImageSpace {
240 &mut self.root
241 }
242
243 pub fn get(&self, id: ImageSpaceId) -> Option<&ImageSpace> {
244 (id.slot == 0).then_some(&self.root)
245 }
246
247 pub fn get_mut(&mut self, id: ImageSpaceId) -> Option<&mut ImageSpace> {
248 (id.slot == 0).then_some(&mut self.root)
249 }
250
251 pub(crate) fn place(
252 &mut self,
253 parent: ImageSpaceId,
254 placement: Placement,
255 ) -> Option<ImageSpaceId> {
256 self.to_root(parent)?;
257 let free = self
258 .placed
259 .iter()
260 .position(|placed| !placed.live && placed.generation != u32::MAX);
261 let (slot, generation) = match free {
262 Some(slot) => {
263 let placed = self.placed.get_mut(slot)?;
264 placed.generation += 1;
265 placed.parent = parent;
266 placed.placement = placement;
267 placed.live = true;
268 (slot, placed.generation)
269 }
270 None => {
271 self.placed.push(Placed {
272 parent,
273 placement,
274 generation: 0,
275 live: true,
276 });
277 (self.placed.len() - 1, 0)
278 }
279 };
280 Some(ImageSpaceId {
281 slot: (slot + 1) as u32,
282 generation,
283 })
284 }
285
286 pub(crate) fn unplace(&mut self, id: ImageSpaceId) {
287 if let Some(placed) = self.slot_mut(id) {
288 placed.live = false;
289 }
290 }
291
292 pub fn placed(&self, id: ImageSpaceId) -> bool {
293 self.slot_of(id).is_some()
294 }
295
296 fn slot_of(&self, id: ImageSpaceId) -> Option<&Placed> {
297 let placed = self.placed.get(id.index().checked_sub(1)?)?;
298 (placed.live && placed.generation == id.generation).then_some(placed)
299 }
300
301 fn slot_mut(&mut self, id: ImageSpaceId) -> Option<&mut Placed> {
302 let placed = self.placed.get_mut(id.index().checked_sub(1)?)?;
303 (placed.generation == id.generation).then_some(placed)
304 }
305
306 pub fn to_root(&self, image: ImageSpaceId) -> Option<Placement> {
308 let mut composed = Placement::Rigid(Rigid::IDENTITY);
309 let mut current = image;
310 while current.slot != 0 {
311 let placed = self.slot_of(current)?;
312 composed = placed.placement.compose(&composed);
313 current = placed.parent;
314 }
315 Some(composed)
316 }
317
318 pub fn ray(&self, image: ImageSpaceId, ndc: [f32; 2]) -> Option<ImageRay> {
319 let root = self.root_ray(ndc)?;
320 Some(self.to_root(image)?.rigid()?.inverse().ray(&root))
321 }
322
323 fn root_ray(&self, ndc: [f32; 2]) -> Option<ImageRay> {
324 let eye = &self.root.eye;
325 let half = (eye.fov_y * 0.5).tan();
326 let direction = Vec3::from(eye.right) * (ndc[0] * half * eye.aspect)
327 + Vec3::from(eye.up) * (ndc[1] * half)
328 + Vec3::from(eye.forward);
329 Some(ImageRay {
330 origin: eye.position,
331 direction: direction.try_normalize()?.to_array(),
332 })
333 }
334
335 pub fn depth(&self, point: [f32; 3]) -> Option<f32> {
337 let eye = &self.root.eye;
338 let (_, forward) = eye.forward_distance(point);
339 (forward >= eye.near).then(|| eye.near / forward)
340 }
341
342 pub fn ndc(&self, point: [f32; 3]) -> Option<[f32; 2]> {
343 let eye = &self.root.eye;
344 let (relative, forward) = eye.forward_distance(point);
345 if forward < eye.near {
346 return None;
347 }
348 let half = (eye.fov_y * 0.5).tan();
349 Some([
350 relative.dot(Vec3::from(eye.right)) / (forward * half * eye.aspect),
351 relative.dot(Vec3::from(eye.up)) / (forward * half),
352 ])
353 }
354
355 pub(crate) fn snapshot(&self) -> ViewsSnapshot {
356 ViewsSnapshot {
357 root_eye: self.root.eye,
358 placed: self.placed.clone(),
359 }
360 }
361
362 pub(crate) fn restore(&mut self, from: &ViewsSnapshot) {
363 self.root.eye = from.root_eye;
364 self.placed.clear();
365 self.placed.extend_from_slice(&from.placed);
366 }
367}
368
369#[derive(Clone, Copy, Debug, PartialEq)]
370pub struct ImageRay {
371 pub origin: [f32; 3],
372 pub direction: [f32; 3],
373}
374
375impl ImageRay {
376 pub fn at(&self, t: f32) -> [f32; 3] {
377 (Vec3::from(self.origin) + Vec3::from(self.direction) * t).to_array()
378 }
379}
380
381pub struct DomainRay<S: Space> {
383 pub origin: S::Point,
384 pub direction: S::Vector,
385}
386
387#[derive(Clone, Copy, Debug, PartialEq)]
389pub struct DepthEnvelope {
390 pub near: f32,
391 pub far: f32,
392}
393
394const UNBOUNDED: DepthEnvelope = DepthEnvelope {
395 near: 0.0,
396 far: f32::INFINITY,
397};
398
399pub trait ViewMapping<S: DomainSpace>: Send + Sync + 'static {
401 fn name(&self) -> &'static str;
402
403 fn image_point(&self, eye: &Pose<S>, point: S::Point) -> Option<[f32; 3]>;
404
405 fn image_local(
406 &self,
407 space: &S,
408 eye: &Pose<S>,
409 pose: &Pose<S>,
410 _relative: &S::Relative,
411 local: S::Point,
412 ) -> Option<[f32; 3]> {
413 self.image_point(eye, space.place(&space.prepare(pose), local).ok()?)
414 }
415
416 fn image_segment(
418 &self,
419 space: &S,
420 eye: &Pose<S>,
421 pose: &Pose<S>,
422 relative: &S::Relative,
423 segment: [S::Point; 2],
424 emit: &mut dyn FnMut(f32, Option<[f32; 3]>),
425 ) {
426 emit(
427 0.0,
428 self.image_local(space, eye, pose, relative, segment[0]),
429 );
430 emit(
431 1.0,
432 self.image_local(space, eye, pose, relative, segment[1]),
433 );
434 }
435
436 fn lift(&self, eye: &Pose<S>, ray: &ImageRay) -> Option<DomainRay<S>>;
437
438 fn section(&self, _eye: &Pose<S>, _pose: &Pose<S>) -> Option<SectionCut> {
439 None
440 }
441
442 fn ray_lift(&self) -> bool {
444 true
445 }
446
447 fn image_radius(&self, _eye: &Pose<S>, _point: S::Point, radius: f32) -> f32 {
448 radius
449 }
450
451 fn depth_envelope(&self) -> DepthEnvelope;
452
453 fn depth_envelope_at(&self, _eye: &Pose<S>) -> DepthEnvelope {
454 self.depth_envelope()
455 }
456}
457
458pub struct ViewStyle<S: DomainSpace> {
459 pub enabled: bool,
460 pub edges: bool,
461 pub section_edges: bool,
462 pub section_faces: bool,
463 pub(crate) mapping: Arc<dyn ViewMapping<S>>,
464}
465
466impl<S: DomainSpace> ViewStyle<S> {
467 pub fn set_mapping(&mut self, mapping: impl ViewMapping<S>) -> &mut Self {
468 self.mapping = Arc::new(mapping);
469 self
470 }
471
472 pub fn mapping(&self) -> &dyn ViewMapping<S> {
473 self.mapping.as_ref()
474 }
475}
476
477impl<S: DomainSpace> Clone for ViewStyle<S> {
478 fn clone(&self) -> Self {
479 Self {
480 enabled: self.enabled,
481 edges: self.edges,
482 section_edges: self.section_edges,
483 section_faces: self.section_faces,
484 mapping: Arc::clone(&self.mapping),
485 }
486 }
487}
488
489pub struct ViewSpec<S: DomainSpace> {
490 pub(crate) image: ImageSpaceId,
491 pub(crate) eye: Entity,
492 pub(crate) subject: Option<Entity>,
493 pub(crate) style: ViewStyle<S>,
494}
495
496impl<S: DomainSpace> Clone for ViewSpec<S> {
497 fn clone(&self) -> Self {
498 Self {
499 image: self.image,
500 eye: self.eye,
501 subject: self.subject,
502 style: self.style.clone(),
503 }
504 }
505}
506
507impl<S: DomainSpace> ViewSpec<S> {
508 pub fn new(image: ImageSpaceId, eye: Entity, mapping: impl ViewMapping<S>) -> Self {
509 Self {
510 image,
511 eye,
512 subject: None,
513 style: ViewStyle {
514 enabled: true,
515 edges: true,
516 section_edges: true,
517 section_faces: true,
518 mapping: Arc::new(mapping),
519 },
520 }
521 }
522
523 pub fn subject(mut self, subject: Entity) -> Self {
524 self.subject = Some(subject);
525 self
526 }
527}
528
529impl<S: DomainSpace> Deref for ViewSpec<S> {
530 type Target = ViewStyle<S>;
531
532 fn deref(&self) -> &Self::Target {
533 &self.style
534 }
535}
536
537impl<S: DomainSpace> DerefMut for ViewSpec<S> {
538 fn deref_mut(&mut self) -> &mut Self::Target {
539 &mut self.style
540 }
541}
542
543#[derive(Clone, Copy, Debug, PartialEq)]
544pub struct SectionCut {
545 pub offset: f32,
547 pub scale: f32,
549}
550
551pub struct Projection4 {
552 pub focal: f32,
553}
554
555impl Projection4 {
556 fn scale(&self, w: f32) -> Option<f32> {
557 let depth = self.focal - w;
558 (depth > 0.0).then(|| self.focal / depth)
559 }
560}
561
562impl ViewMapping<EuclideanR4> for Projection4 {
563 fn name(&self) -> &'static str {
564 "projection4"
565 }
566
567 fn image_point(
568 &self,
569 eye: &Pose<EuclideanR4>,
570 point: <EuclideanR4 as Space>::Point,
571 ) -> Option<[f32; 3]> {
572 let relative = eye_relative4(eye, point)?;
573 let scale = self.scale(relative.w)?;
574 Some((relative.truncate() * scale).to_array())
575 }
576
577 fn image_local(
578 &self,
579 space: &EuclideanR4,
580 _eye: &Pose<EuclideanR4>,
581 _pose: &Pose<EuclideanR4>,
582 relative: &<EuclideanR4 as DomainSpace>::Relative,
583 local: Vec4,
584 ) -> Option<[f32; 3]> {
585 let relative = space.place_relative(relative, local).ok()?;
586 let scale = self.scale(relative.w)?;
587 Some((relative.truncate() * scale).to_array())
588 }
589
590 fn lift(&self, _eye: &Pose<EuclideanR4>, _ray: &ImageRay) -> Option<DomainRay<EuclideanR4>> {
591 None
592 }
593
594 fn ray_lift(&self) -> bool {
595 false
596 }
597
598 fn image_radius(
599 &self,
600 eye: &Pose<EuclideanR4>,
601 point: <EuclideanR4 as Space>::Point,
602 radius: f32,
603 ) -> f32 {
604 eye_relative4(eye, point)
605 .and_then(|relative| self.scale(relative.w))
606 .map_or(radius, |scale| radius * scale)
607 }
608
609 fn depth_envelope(&self) -> DepthEnvelope {
610 UNBOUNDED
611 }
612}
613
614pub struct Section4 {
616 pub w: f32,
617}
618
619impl ViewMapping<EuclideanR4> for Section4 {
620 fn name(&self) -> &'static str {
621 "section4"
622 }
623
624 fn image_point(
625 &self,
626 eye: &Pose<EuclideanR4>,
627 point: <EuclideanR4 as Space>::Point,
628 ) -> Option<[f32; 3]> {
629 Some(eye_relative4(eye, point)?.truncate().to_array())
630 }
631
632 fn image_local(
633 &self,
634 space: &EuclideanR4,
635 _eye: &Pose<EuclideanR4>,
636 _pose: &Pose<EuclideanR4>,
637 relative: &<EuclideanR4 as DomainSpace>::Relative,
638 local: Vec4,
639 ) -> Option<[f32; 3]> {
640 Some(
641 space
642 .place_relative(relative, local)
643 .ok()?
644 .truncate()
645 .to_array(),
646 )
647 }
648
649 fn section(&self, eye: &Pose<EuclideanR4>, pose: &Pose<EuclideanR4>) -> Option<SectionCut> {
650 Some(SectionCut {
651 offset: self.w - eye_relative4(eye, pose.point)?.w,
652 scale: 1.0,
653 })
654 }
655
656 fn lift(&self, eye: &Pose<EuclideanR4>, ray: &ImageRay) -> Option<DomainRay<EuclideanR4>> {
657 let direction = Vec3::from(ray.direction).try_normalize()?;
658 let origin = Vec3::from(ray.origin).extend(self.w);
659 Some(DomainRay {
660 origin: EuclideanR4.place(&EuclideanR4.prepare(eye), origin).ok()?,
661 direction: EuclideanR4.carry(eye, origin, direction.extend(0.0)).ok()?,
662 })
663 }
664
665 fn depth_envelope(&self) -> DepthEnvelope {
666 UNBOUNDED
667 }
668}
669
670fn eye_relative4(eye: &Pose<EuclideanR4>, point: Vec4) -> Option<Vec4> {
671 EuclideanR4.local(eye, point).ok()
672}
673
674pub struct Klein;
675
676impl ViewMapping<HyperbolicH3> for Klein {
677 fn name(&self) -> &'static str {
678 "klein"
679 }
680
681 fn image_point(
682 &self,
683 eye: &Pose<HyperbolicH3>,
684 point: <HyperbolicH3 as Space>::Point,
685 ) -> Option<[f32; 3]> {
686 Some(poincare_to_klein(HyperbolicH3.local(eye, point).ok()?).to_array())
688 }
689
690 fn image_local(
691 &self,
692 space: &HyperbolicH3,
693 _eye: &Pose<HyperbolicH3>,
694 _pose: &Pose<HyperbolicH3>,
695 relative: &<HyperbolicH3 as DomainSpace>::Relative,
696 local: Vec3,
697 ) -> Option<[f32; 3]> {
698 Some(poincare_to_klein(space.place_relative(relative, local).ok()?).to_array())
699 }
700
701 fn lift(&self, eye: &Pose<HyperbolicH3>, ray: &ImageRay) -> Option<DomainRay<HyperbolicH3>> {
702 let origin = Vec3::from(ray.origin);
703 let direction = Vec3::from(ray.direction).try_normalize()?;
704 let inside = 1.0 - origin.length_squared();
705 if inside <= 0.0 {
706 return None;
707 }
708 let along = origin.dot(direction);
709 let exit = -along + (along * along + inside).sqrt();
710 let from = klein_to_poincare(origin);
711 let toward = klein_to_poincare(origin + direction * (exit * 0.5));
712 let length = HyperbolicH3.distance(from, toward);
713 if length <= 0.0 {
714 return None;
715 }
716 let unit = HyperbolicH3.log(from, toward) * (1.0 / length);
717 Some(DomainRay {
718 origin: HyperbolicH3.place(&HyperbolicH3.prepare(eye), from).ok()?,
719 direction: HyperbolicH3.carry(eye, from, unit).ok()?,
720 })
721 }
722
723 fn depth_envelope(&self) -> DepthEnvelope {
724 klein_depth_envelope(HyperbolicH3.chart_envelope())
725 }
726
727 fn depth_envelope_at(&self, eye: &Pose<HyperbolicH3>) -> DepthEnvelope {
728 klein_depth_envelope(HyperbolicH3.distance(Vec3::ZERO, eye.point))
729 }
730}
731
732pub fn klein_depth_envelope(eye_chart_distance: f32) -> DepthEnvelope {
733 DepthEnvelope {
734 near: 0.0,
735 far: (H3_EYE_CHART_REACH + H3_DEPTH_ENVELOPE - eye_chart_distance)
736 .clamp(0.0, H3_DEPTH_ENVELOPE),
737 }
738}
739
740pub struct Identity3;
741
742impl ViewMapping<EuclideanR3> for Identity3 {
743 fn name(&self) -> &'static str {
744 "identity3"
745 }
746
747 fn image_point(
748 &self,
749 eye: &Pose<EuclideanR3>,
750 point: <EuclideanR3 as Space>::Point,
751 ) -> Option<[f32; 3]> {
752 Some(EuclideanR3.local(eye, point).ok()?.to_array())
753 }
754
755 fn image_local(
756 &self,
757 space: &EuclideanR3,
758 _eye: &Pose<EuclideanR3>,
759 _pose: &Pose<EuclideanR3>,
760 relative: &<EuclideanR3 as DomainSpace>::Relative,
761 local: Vec3,
762 ) -> Option<[f32; 3]> {
763 Some(space.place_relative(relative, local).ok()?.to_array())
764 }
765
766 fn lift(&self, eye: &Pose<EuclideanR3>, ray: &ImageRay) -> Option<DomainRay<EuclideanR3>> {
767 let origin = Vec3::from(ray.origin);
768 let direction = Vec3::from(ray.direction).try_normalize()?;
769 Some(DomainRay {
770 origin: EuclideanR3.place(&EuclideanR3.prepare(eye), origin).ok()?,
771 direction: EuclideanR3.carry(eye, origin, direction).ok()?,
772 })
773 }
774
775 fn depth_envelope(&self) -> DepthEnvelope {
776 UNBOUNDED
777 }
778}
779
780pub(crate) fn ball_entry(along: f32, outside: f32) -> Option<f32> {
781 if outside <= 0.0 {
782 return Some(0.0);
783 }
784 let discriminant = along * along - outside;
785 if discriminant < 0.0 {
786 return None;
787 }
788 let entry = -along - discriminant.sqrt();
789 (entry >= 0.0).then_some(entry)
790}
791
792pub(crate) fn image_hit(ray: &ImageRay, center: [f32; 3], radius: f32) -> Option<f32> {
793 let offset = Vec3::from(ray.origin) - Vec3::from(center);
794 ball_entry(
795 offset.dot(Vec3::from(ray.direction)),
796 offset.length_squared() - radius * radius,
797 )
798}
799
800#[derive(Clone, Copy, Debug, PartialEq)]
801pub struct InstanceRecord {
802 pub entity: Entity,
803 pub geometry: PreparedId,
804 pub material: MaterialId,
805 pub pose: ChartPose,
806 pub image_point: [f32; 3],
807}
808
809#[repr(C)]
811#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
812pub struct SegmentRecord {
813 pub start: [f32; 3],
814 pub _pad0: f32,
815 pub end: [f32; 3],
816 pub _pad1: f32,
817 pub start_color: [f32; 4],
818 pub end_color: [f32; 4],
819 pub width_px: f32,
820 pub _pad2: [f32; 3],
821}
822
823#[repr(C)]
825#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
826pub struct PointRecord {
827 pub position: [f32; 3],
828 pub radius_px: f32,
829 pub color: [f32; 4],
830}
831
832#[repr(C)]
833#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
834pub struct TriangleRecord {
835 pub vertices: [[f32; 3]; 3],
836 pub color: [f32; 4],
837}
838
839#[derive(Default)]
840pub(crate) struct SectionScratchpad {
841 pub(crate) rotated: Vec<Vec4>,
842 pub(crate) cut: SectionScratch,
843 pub(crate) faces: TriangleMesh<3>,
844 pub(crate) perimeter: LineMesh<3>,
845}
846
847#[derive(Clone, Copy, Debug, PartialEq, Eq)]
848pub enum RefusalSource {
849 Instance,
850 Segment,
851 Section,
852}
853
854#[derive(Clone, Copy, Debug, PartialEq, Eq)]
855pub struct ViewRefusal {
856 pub entity: Entity,
857 pub error: DomainError,
858 pub source: RefusalSource,
859}
860
861#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
862pub struct ViewRefusals {
863 pub count: u32,
864 pub first: Option<ViewRefusal>,
865 pub last: Option<ViewRefusal>,
866}
867
868impl ViewRefusals {
869 pub(crate) fn record(&mut self, entity: Entity, error: DomainError, source: RefusalSource) {
870 let refusal = ViewRefusal {
871 entity,
872 error,
873 source,
874 };
875 self.count = self.count.saturating_add(1);
876 self.first.get_or_insert(refusal);
877 self.last = Some(refusal);
878 }
879}
880
881const NO_OUTPUT: u32 = u32::MAX;
882
883#[derive(Clone)]
884pub(crate) struct EntityOutput {
885 pub(crate) entity: Entity,
886 pub(crate) section_segments: Range<usize>,
887 pub(crate) line_segments: Range<usize>,
888 pub(crate) triangles: Range<usize>,
889 pub(crate) instance: bool,
890}
891
892#[derive(Default)]
893pub(crate) struct ViewOutputCache {
894 entries: Vec<EntityOutput>,
895 positions: Vec<u32>,
896}
897
898impl ViewOutputCache {
899 pub(crate) fn clear(&mut self) {
900 self.entries.clear();
901 self.positions.fill(NO_OUTPUT);
902 }
903
904 pub(crate) fn push(&mut self, output: EntityOutput) {
905 let slot = output.entity.key().slot() as usize;
906 if slot >= self.positions.len() {
907 self.positions.resize(slot + 1, NO_OUTPUT);
908 }
909 self.positions[slot] = self.entries.len() as u32;
910 self.entries.push(output);
911 }
912
913 pub(crate) fn get(&self, entity: Entity) -> Option<&EntityOutput> {
914 let position = *self.positions.get(entity.key().slot() as usize)?;
915 if position == NO_OUTPUT || self.entries[position as usize].entity != entity {
916 return None;
917 }
918 Some(&self.entries[position as usize])
919 }
920
921 pub(crate) fn iter_mut(&mut self) -> impl Iterator<Item = &mut EntityOutput> {
922 self.entries.iter_mut()
923 }
924}
925
926#[derive(Default)]
927pub struct ViewRecords {
928 pub instances: RecordBuffer<InstanceRecord>,
929 pub(crate) segments: Vec<SegmentRecord>,
930 pub(crate) triangles: Vec<TriangleRecord>,
931 pub(crate) refusals: ViewRefusals,
932 pub(crate) scratch: SectionScratchpad,
933 pub(crate) output: ViewOutputCache,
934 pub(crate) patch_segments: Vec<SegmentRecord>,
935 pub(crate) patch_triangles: Vec<TriangleRecord>,
936 pub(crate) changed: Vec<Entity>,
937 pub(crate) poses: Cursor,
938 pub(crate) attachments: Cursor,
939 pub(crate) revision: u32,
940 pub(crate) built: Stamp,
941 pub(crate) alpha: f32,
942 pub(crate) merged: Vec<Entity>,
943 pub(crate) generation: u64,
944}
945
946impl ViewRecords {
947 pub fn segments(&self) -> &[SegmentRecord] {
948 &self.segments
949 }
950
951 pub fn triangles(&self) -> &[TriangleRecord] {
952 &self.triangles
953 }
954
955 pub fn refusals(&self) -> ViewRefusals {
956 self.refusals
957 }
958
959 pub fn built(&self) -> Stamp {
961 self.built
962 }
963}
964
965#[derive(Clone, Copy, Debug, PartialEq)]
967pub struct Pick {
968 pub entity: Entity,
969 pub domain: DomainId,
970 pub view: ViewId,
971 pub image: ImageSpaceId,
972 pub image_point: [f32; 3],
973 pub depth: f32,
974 pub hit: Option<ChartPoint>,
975}
976
977#[derive(Clone, Copy, Debug, PartialEq, Eq)]
978pub struct ViewSummary {
979 pub name: &'static str,
980 pub eye: Entity,
981 pub image: ImageSpaceId,
982 pub ray_lift: bool,
983}