1use std::collections::HashMap;
3use std::fmt::{Display, Formatter};
4use std::ops::Range;
5
6use anyhow::{anyhow, Result};
7use serde::{Deserialize, Serialize};
8
9use crate::core::{
10 math::{mat3::Mat3, vec3::Vec3},
11 Cursor, Float, RefractiveIndex,
12};
13use crate::specs::{
14 gaps::GapSpec,
15 surfaces::{SurfaceSpec, SurfaceType},
16};
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
21pub enum Axis {
22 X,
23 Y,
24}
25
26#[derive(Debug)]
28pub struct Gap {
29 pub thickness: Float,
30 pub refractive_index: RefractiveIndex,
31}
32
33#[derive(Debug)]
42pub struct SequentialModel {
43 surfaces: Vec<Surface>,
44 submodels: HashMap<SubModelID, SequentialSubModelBase>,
45}
46
47pub trait SequentialSubModel {
115 fn gaps(&self) -> &[Gap];
116 fn is_obj_at_inf(&self) -> bool;
117
118 fn is_empty(&self) -> bool {
119 self.gaps().is_empty()
120 }
121 fn len(&self) -> usize {
122 self.gaps().len()
123 }
124 fn try_iter<'a>(&'a self, surfaces: &'a [Surface]) -> Result<SequentialSubModelIter<'a>>;
125
126 fn slice(&self, idx: Range<usize>) -> SequentialSubModelSlice<'_> {
127 SequentialSubModelSlice {
128 gaps: &self.gaps()[idx],
129 }
130 }
131}
132
133#[derive(Debug)]
134pub struct SequentialSubModelBase {
135 gaps: Vec<Gap>,
136}
137
138#[derive(Debug)]
142pub struct SequentialSubModelSlice<'a> {
143 gaps: &'a [Gap],
144}
145
146#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
155pub struct SubModelID(pub Option<usize>, pub Axis);
156
157pub struct SequentialSubModelIter<'a> {
161 surfaces: &'a [Surface],
162 gaps: &'a [Gap],
163 index: usize,
164}
165
166pub struct SequentialSubModelReverseIter<'a> {
168 surfaces: &'a [Surface],
169 gaps: &'a [Gap],
170 index: usize,
171}
172
173pub type Step<'a> = (&'a Gap, &'a Surface, Option<&'a Gap>);
178
179#[derive(Debug)]
180pub enum Surface {
181 Conic(Conic),
182 Image(Image),
183 Object(Object),
184 Probe(Probe),
185 Stop(Stop),
186 }
188
189#[derive(Debug)]
190pub struct Conic {
191 pos: Vec3,
192 rotation_matrix: Mat3,
193 semi_diameter: Float,
194 radius_of_curvature: Float,
195 conic_constant: Float,
196 surface_type: SurfaceType,
197}
198
199#[derive(Debug)]
200pub struct Image {
201 pos: Vec3,
202 rotation_matrix: Mat3,
203}
204
205#[derive(Debug)]
206pub struct Object {
207 pos: Vec3,
208 rotation_matrix: Mat3,
209}
210
211#[derive(Debug)]
213pub struct Probe {
214 pos: Vec3,
215 rotation_matrix: Mat3,
216}
217
218#[derive(Debug)]
219pub struct Stop {
220 pos: Vec3,
221 rotation_matrix: Mat3,
222 semi_diameter: Float,
223}
224
225pub(crate) fn first_physical_surface(surfaces: &[Surface]) -> Option<usize> {
241 surfaces
242 .iter()
243 .position(|surf| matches!(surf, Surface::Conic(_) | Surface::Stop(_)))
244}
245
246pub fn last_physical_surface(surfaces: &[Surface]) -> Option<usize> {
250 surfaces
251 .iter()
252 .rposition(|surf| matches!(surf, Surface::Conic(_) | Surface::Stop(_)))
253}
254
255pub fn reversed_surface_id(surfaces: &[Surface], surf_id: usize) -> usize {
257 surfaces.len() - surf_id - 1
259}
260
261impl Conic {
262 pub fn sag_norm(&self, pos: Vec3) -> (Float, Vec3) {
263 if self.radius_of_curvature.is_infinite() {
264 return (0.0, Vec3::new(0.0, 0.0, 1.0));
265 }
266
267 let r = (pos.x().powi(2) + pos.y().powi(2)).sqrt();
269 let theta = pos.y().atan2(pos.x());
270
271 let a = r.powi(2) / self.radius_of_curvature;
273 let sag =
274 a / (1.0 + (1.0 - (1.0 + self.conic_constant) * a / self.radius_of_curvature).sqrt());
275
276 let denom = (self.radius_of_curvature.powi(4)
278 - (1.0 + self.conic_constant) * (r * self.radius_of_curvature).powi(2))
279 .sqrt();
280 let dfdx = -r * self.radius_of_curvature * theta.cos() / denom;
281 let dfdy = -r * self.radius_of_curvature * theta.sin() / denom;
282 let dfdz = 1.0 as Float;
283 let norm = Vec3::new(dfdx, dfdy, dfdz).normalize();
284
285 (sag, norm)
286 }
287}
288
289impl Gap {
290 pub(crate) fn try_from_spec(spec: &GapSpec, wavelength: Option<Float>) -> Result<Self> {
291 let thickness = spec.thickness;
292 let refractive_index = RefractiveIndex::try_from_spec(&spec.refractive_index, wavelength)?;
293 Ok(Self {
294 thickness,
295 refractive_index,
296 })
297 }
298}
299
300impl SequentialModel {
301 pub fn new(
311 gap_specs: &[GapSpec],
312 surface_specs: &[SurfaceSpec],
313 wavelengths: &[Float],
314 ) -> Result<Self> {
315 Self::validate_specs(gap_specs, wavelengths)?;
316
317 let surfaces = Self::surf_specs_to_surfs(surface_specs, gap_specs);
318
319 let model_ids: Vec<SubModelID> = Self::calc_model_ids(&surfaces, wavelengths);
320 let mut models: HashMap<SubModelID, SequentialSubModelBase> = HashMap::new();
321 for model_id in model_ids.iter() {
322 let wavelength = model_id.0.map(|idx| wavelengths[idx]);
323 let gaps = Self::gap_specs_to_gaps(gap_specs, wavelength)?;
324 let model = SequentialSubModelBase::new(gaps);
325 models.insert(*model_id, model);
326 }
327
328 Ok(Self {
329 surfaces,
330 submodels: models,
331 })
332 }
333
334 pub fn surfaces(&self) -> &[Surface] {
336 &self.surfaces
337 }
338
339 pub fn submodels(&self) -> &HashMap<SubModelID, impl SequentialSubModel> {
341 &self.submodels
342 }
343
344 pub fn largest_semi_diameter(&self) -> Float {
349 self.surfaces
350 .iter()
351 .filter_map(|surf| match surf {
352 Surface::Conic(conic) => Some(conic.semi_diameter),
353 Surface::Stop(stop) => Some(stop.semi_diameter),
355 _ => None,
356 })
357 .fold(0.0, |acc, x| acc.max(x))
358 }
359
360 fn calc_model_ids(surfaces: &[Surface], wavelengths: &[Float]) -> Vec<SubModelID> {
362 let mut ids = Vec::new();
363 if wavelengths.is_empty() && Self::is_rotationally_symmetric(surfaces) {
364 ids.push(SubModelID(None, Axis::Y));
365 return ids;
366 } else if wavelengths.is_empty() {
367 ids.push(SubModelID(None, Axis::X));
368 ids.push(SubModelID(None, Axis::Y));
369 return ids;
370 }
371
372 let axes: Vec<Axis> = if Self::is_rotationally_symmetric(surfaces) {
373 vec![Axis::Y]
374 } else {
375 vec![Axis::X, Axis::Y]
376 };
377
378 for (idx, _wavelength) in wavelengths.iter().enumerate() {
379 for axis in axes.iter() {
380 let id = SubModelID(Some(idx), *axis);
381 ids.push(id);
382 }
383 }
384 ids
385 }
386
387 fn gap_specs_to_gaps(gap_specs: &[GapSpec], wavelength: Option<Float>) -> Result<Vec<Gap>> {
388 let mut gaps = Vec::new();
389 for gap_spec in gap_specs.iter() {
390 let gap = Gap::try_from_spec(gap_spec, wavelength)?;
391 gaps.push(gap);
392 }
393 Ok(gaps)
394 }
395
396 fn is_rotationally_symmetric(_surfaces: &[Surface]) -> bool {
399 true
404 }
405
406 fn surf_specs_to_surfs(surf_specs: &[SurfaceSpec], gap_specs: &[GapSpec]) -> Vec<Surface> {
407 let mut surfaces = Vec::new();
408
409 let mut cursor = Cursor::new(-gap_specs[0].thickness);
412
413 for (surf_spec, gap_spec) in surf_specs.iter().zip(gap_specs.iter()) {
415 let surf = Surface::from_spec(surf_spec, cursor.pos());
416
417 if let SurfaceType::Reflecting = surf.surface_type() {
419 cursor.invert();
420 }
421
422 surfaces.push(surf);
424 cursor.advance(gap_spec.thickness);
425 }
426
427 surfaces.push(Surface::from_spec(
429 surf_specs
430 .last()
431 .expect("There should always be one last surface."),
432 cursor.pos(),
433 ));
434 surfaces
435 }
436
437 fn validate_gaps(gaps: &[GapSpec], wavelengths: &[Float]) -> Result<()> {
438 if gaps.is_empty() {
439 return Err(anyhow!("The system must have at least one gap."));
440 }
441
442 if wavelengths.is_empty() {
445 for gap in gaps.iter() {
446 if gap.refractive_index.depends_on_wavelength() {
447 return Err(anyhow!(
448 "The refractive index of the gap must be a constant when no wavelengths are provided."
449 ));
450 }
451 }
452 }
453 Ok(())
454 }
455
456 fn validate_specs(gaps: &[GapSpec], wavelengths: &[Float]) -> Result<()> {
457 Self::validate_gaps(gaps, wavelengths)?;
459 Ok(())
460 }
461}
462
463impl SequentialSubModelBase {
464 pub(crate) fn new(gaps: Vec<Gap>) -> Self {
465 Self { gaps }
466 }
467}
468
469impl SequentialSubModel for SequentialSubModelBase {
470 fn gaps(&self) -> &[Gap] {
471 &self.gaps
472 }
473
474 fn is_obj_at_inf(&self) -> bool {
475 self.gaps
476 .first()
477 .expect("There must be at least one gap in a sequential submodel.")
478 .thickness
479 .is_infinite()
480 }
481
482 fn try_iter<'a>(&'a self, surfaces: &'a [Surface]) -> Result<SequentialSubModelIter<'a>> {
483 SequentialSubModelIter::new(surfaces, &self.gaps)
484 }
485}
486
487impl<'a> SequentialSubModel for SequentialSubModelSlice<'a> {
488 fn gaps(&self) -> &[Gap] {
489 self.gaps
490 }
491
492 fn is_obj_at_inf(&self) -> bool {
493 self.gaps
494 .first()
495 .expect("There must be at least one gap in a sequential submodel.")
496 .thickness
497 .is_infinite()
498 }
499
500 fn try_iter<'b>(&'b self, surfaces: &'b [Surface]) -> Result<SequentialSubModelIter<'b>> {
501 SequentialSubModelIter::new(surfaces, self.gaps)
502 }
503}
504
505impl<'a> SequentialSubModelIter<'a> {
506 fn new(surfaces: &'a [Surface], gaps: &'a [Gap]) -> Result<Self> {
507 if surfaces.len() != gaps.len() + 1 {
508 return Err(anyhow!(
509 "The number of surfaces must be one more than the number of gaps in a forward sequential submodel."
510 ));
511 }
512
513 Ok(Self {
514 surfaces,
515 gaps,
516 index: 0,
517 })
518 }
519
520 pub fn try_reverse(self) -> Result<SequentialSubModelReverseIter<'a>> {
521 SequentialSubModelReverseIter::new(self.surfaces, self.gaps)
522 }
523}
524
525impl<'a> Iterator for SequentialSubModelIter<'a> {
526 type Item = Step<'a>;
527
528 fn next(&mut self) -> Option<Self::Item> {
529 if self.index == self.gaps.len() - 1 {
530 let result = Some((&self.gaps[self.index], &self.surfaces[self.index + 1], None));
532 self.index += 1;
533 result
534 } else if self.index < self.gaps.len() {
535 let result = Some((
536 &self.gaps[self.index],
537 &self.surfaces[self.index + 1],
538 Some(&self.gaps[self.index + 1]),
539 ));
540 self.index += 1;
541 result
542 } else {
543 None
544 }
545 }
546}
547
548impl<'a> ExactSizeIterator for SequentialSubModelIter<'a> {
549 fn len(&self) -> usize {
550 self.gaps.len()
551 }
552}
553
554impl<'a> SequentialSubModelReverseIter<'a> {
555 fn new(surfaces: &'a [Surface], gaps: &'a [Gap]) -> Result<Self> {
556 if surfaces.len() != gaps.len() + 1 {
558 return Err(anyhow!(
559 "The number of surfaces must be one more than the number of gaps in a reversed sequential submodel."
560 ));
561 }
562
563 Ok(Self {
564 surfaces,
565 gaps,
566 index: 1,
568 })
569 }
570}
571
572impl<'a> Iterator for SequentialSubModelReverseIter<'a> {
573 type Item = Step<'a>;
574
575 fn next(&mut self) -> Option<Self::Item> {
576 let n = self.gaps.len();
578 let forward_index = n - self.index;
579 if self.index < n {
580 let result = Some((
582 &self.gaps[forward_index],
583 &self.surfaces[forward_index],
584 Some(&self.gaps[forward_index - 1]),
585 ));
586 self.index += 1;
587 result
588 } else {
589 None
590 }
591 }
592}
593
594impl Surface {
595 pub fn z(&self) -> Float {
597 self.pos().z()
598 }
599
600 pub(crate) fn from_spec(spec: &SurfaceSpec, pos: Vec3) -> Self {
601 let euler_angles = Vec3::new(0.0, 0.0, 0.0);
603 let rotation_matrix =
604 Mat3::from_euler_angles(euler_angles.x(), euler_angles.y(), euler_angles.z());
605
606 match spec {
607 SurfaceSpec::Conic {
608 semi_diameter,
609 radius_of_curvature,
610 conic_constant,
611 surf_type,
612 } => Self::Conic(Conic {
613 pos,
614 rotation_matrix,
615 semi_diameter: *semi_diameter,
616 radius_of_curvature: *radius_of_curvature,
617 conic_constant: *conic_constant,
618 surface_type: *surf_type,
619 }),
620 SurfaceSpec::Image => Self::Image(Image {
621 pos,
622 rotation_matrix,
623 }),
624 SurfaceSpec::Object => Self::Object(Object {
625 pos,
626 rotation_matrix,
627 }),
628 SurfaceSpec::Probe => Self::Probe(Probe {
629 pos,
630 rotation_matrix,
631 }),
632 SurfaceSpec::Stop { semi_diameter } => Self::Stop(Stop {
633 pos,
634 rotation_matrix,
635 semi_diameter: *semi_diameter,
636 }),
637 }
653 }
654
655 pub(crate) fn outside_clear_aperture(&self, pos: Vec3) -> bool {
660 let r_transv = pos.x() * pos.x() + pos.y() * pos.y();
661 let r_max = self.semi_diameter();
662
663 r_transv > r_max * r_max
664 }
665
666 pub(crate) fn roc(&self, axis: &Axis) -> Float {
667 match axis {
668 Axis::X => self.rocx(),
669 Axis::Y => self.rocy(),
670 }
671 }
672
673 fn rocx(&self) -> Float {
675 match self {
676 Self::Conic(conic) => conic.radius_of_curvature,
677 _ => Float::INFINITY,
679 }
680 }
681
682 fn rocy(&self) -> Float {
684 match self {
685 Self::Conic(conic) => conic.radius_of_curvature,
686 _ => Float::INFINITY,
688 }
689 }
690
691 pub(crate) fn rot_mat(&self) -> Mat3 {
694 match self {
695 Self::Conic(conic) => conic.rotation_matrix,
696 Self::Image(image) => image.rotation_matrix,
697 Self::Object(object) => object.rotation_matrix,
698 Self::Probe(probe) => probe.rotation_matrix,
699 Self::Stop(stop) => stop.rotation_matrix,
700 }
702 }
703
704 pub(crate) fn pos(&self) -> Vec3 {
706 match self {
707 Self::Conic(conic) => conic.pos,
708 Self::Image(image) => image.pos,
709 Self::Object(object) => object.pos,
710 Self::Probe(probe) => probe.pos,
711 Self::Stop(stop) => stop.pos,
712 }
714 }
715
716 pub(crate) fn sag_norm(&self, pos: Vec3) -> (Float, Vec3) {
721 match self {
722 Self::Conic(conic) => conic.sag_norm(pos),
723 Self::Image(_) | Self::Object(_) | Self::Probe(_) | Self::Stop(_) => {
725 (0.0, Vec3::new(0.0, 0.0, 1.0))
726 } }
728 }
729
730 pub(crate) fn semi_diameter(&self) -> Float {
731 match self {
732 Self::Conic(conic) => conic.semi_diameter,
733 Self::Stop(stop) => stop.semi_diameter,
735 _ => Float::INFINITY,
736 }
737 }
738
739 pub(crate) fn surface_type(&self) -> SurfaceType {
740 match self {
741 Self::Conic(conic) => conic.surface_type,
742 _ => SurfaceType::NoOp,
744 }
745 }
746}
747
748impl Display for Surface {
749 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
750 match self {
751 Self::Conic(_) => write!(f, "Conic"),
752 Self::Image(_) => write!(f, "Image"),
753 Self::Object(_) => write!(f, "Object"),
754 Self::Probe(_) => write!(f, "Probe"),
755 Self::Stop(_) => write!(f, "Stop"),
756 }
758 }
759}
760
761#[cfg(test)]
762mod tests {
763 use super::*;
764 use crate::{
765 core::math::mat3::Mat3,
766 examples::convexplano_lens::sequential_model,
767 specs::gaps::{RealSpec, RefractiveIndexSpec},
768 };
769
770 #[test]
771 fn gaps_must_specify_ri_when_no_wavelengths_provided() {
772 let gaps = vec![
773 GapSpec {
774 thickness: 1.0,
775 refractive_index: RefractiveIndexSpec {
776 real: RealSpec::Constant(1.0),
777 imag: None,
778 },
779 },
780 GapSpec {
781 thickness: 1.0,
782 refractive_index: RefractiveIndexSpec {
783 real: RealSpec::Formula2 {
784 wavelength_range: [0.3, 0.8],
785 coefficients: vec![1.0, 2.0, 3.0, 4.0],
786 },
787 imag: None,
788 },
789 },
790 ];
791 let wavelengths = Vec::new();
792
793 let result = SequentialModel::validate_gaps(&gaps, &wavelengths);
794 assert!(result.is_err());
795 }
796
797 #[test]
798 fn is_rotationally_symmetric() {
799 let surfaces = vec![
800 Surface::Conic(Conic {
801 pos: Vec3::new(0.0, 0.0, 0.0),
802 rotation_matrix: Mat3::new(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0),
803 semi_diameter: 1.0,
804 radius_of_curvature: 1.0,
805 conic_constant: 0.0,
806 surface_type: SurfaceType::Refracting,
807 }),
808 Surface::Conic(Conic {
809 pos: Vec3::new(0.0, 0.0, 0.0),
810 rotation_matrix: Mat3::new(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0),
811 semi_diameter: 1.0,
812 radius_of_curvature: 1.0,
813 conic_constant: 0.0,
814 surface_type: SurfaceType::Refracting,
815 }),
816 ];
817 assert!(SequentialModel::is_rotationally_symmetric(&surfaces));
818
819 }
840
841 #[test]
842 fn test_calc_model_ids() {
843 let sequential_model = sequential_model();
844 let surfaces = sequential_model.surfaces();
845 let wavelengths = vec![0.4, 0.6];
846
847 let model_ids = SequentialModel::calc_model_ids(surfaces, &wavelengths);
848
849 assert_eq!(model_ids.len(), 2); }
852
853 #[test]
854 fn test_calc_model_ids_no_wavelength() {
855 let sequential_model = sequential_model();
856 let surfaces = sequential_model.surfaces();
857 let wavelengths = Vec::new();
858
859 let model_ids = SequentialModel::calc_model_ids(surfaces, &wavelengths);
860
861 assert_eq!(model_ids.len(), 1); }
863
864 #[test]
865 fn test_first_physical_surface() {
866 let surfaces = vec![
867 Surface::Object(Object {
868 pos: Vec3::new(0.0, 0.0, 0.0),
869 rotation_matrix: Mat3::new(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0),
870 }),
871 Surface::Probe(Probe {
872 pos: Vec3::new(0.0, 0.0, 0.0),
873 rotation_matrix: Mat3::new(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0),
874 }),
875 Surface::Conic(Conic {
876 pos: Vec3::new(0.0, 0.0, 0.0),
877 rotation_matrix: Mat3::new(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0),
878 semi_diameter: 1.0,
879 radius_of_curvature: 1.0,
880 conic_constant: 0.0,
881 surface_type: SurfaceType::Refracting,
882 }),
883 Surface::Conic(Conic {
884 pos: Vec3::new(0.0, 0.0, 0.0),
885 rotation_matrix: Mat3::new(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0),
886 semi_diameter: 1.0,
887 radius_of_curvature: 1.0,
888 conic_constant: 0.0,
889 surface_type: SurfaceType::Refracting,
890 }),
891 Surface::Image(Image {
892 pos: Vec3::new(0.0, 0.0, 0.0),
893 rotation_matrix: Mat3::new(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0),
894 }),
895 ];
896
897 let result = first_physical_surface(&surfaces);
898 assert_eq!(result, Some(2));
899 }
900
901 #[test]
902 fn test_last_physical_surface() {
903 let surfaces = vec![
904 Surface::Object(Object {
905 pos: Vec3::new(0.0, 0.0, 0.0),
906 rotation_matrix: Mat3::new(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0),
907 }),
908 Surface::Conic(Conic {
909 pos: Vec3::new(0.0, 0.0, 0.0),
910 rotation_matrix: Mat3::new(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0),
911 semi_diameter: 1.0,
912 radius_of_curvature: 1.0,
913 conic_constant: 0.0,
914 surface_type: SurfaceType::Refracting,
915 }),
916 Surface::Conic(Conic {
917 pos: Vec3::new(0.0, 0.0, 0.0),
918 rotation_matrix: Mat3::new(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0),
919 semi_diameter: 1.0,
920 radius_of_curvature: 1.0,
921 conic_constant: 0.0,
922 surface_type: SurfaceType::Refracting,
923 }),
924 Surface::Probe(Probe {
925 pos: Vec3::new(0.0, 0.0, 0.0),
926 rotation_matrix: Mat3::new(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0),
927 }),
928 Surface::Image(Image {
929 pos: Vec3::new(0.0, 0.0, 0.0),
930 rotation_matrix: Mat3::new(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0),
931 }),
932 ];
933
934 let result = last_physical_surface(&surfaces);
935 assert_eq!(result, Some(2));
936 }
937
938 #[test]
939 fn test_reversed_surface_id() {
940 let surfaces = vec![
941 Surface::Object(Object {
942 pos: Vec3::new(0.0, 0.0, 0.0),
943 rotation_matrix: Mat3::new(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0),
944 }),
945 Surface::Conic(Conic {
946 pos: Vec3::new(0.0, 0.0, 0.0),
947 rotation_matrix: Mat3::new(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0),
948 semi_diameter: 1.0,
949 radius_of_curvature: 1.0,
950 conic_constant: 0.0,
951 surface_type: SurfaceType::Refracting,
952 }),
953 Surface::Conic(Conic {
954 pos: Vec3::new(0.0, 0.0, 0.0),
955 rotation_matrix: Mat3::new(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0),
956 semi_diameter: 1.0,
957 radius_of_curvature: 1.0,
958 conic_constant: 0.0,
959 surface_type: SurfaceType::Refracting,
960 }),
961 Surface::Probe(Probe {
962 pos: Vec3::new(0.0, 0.0, 0.0),
963 rotation_matrix: Mat3::new(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0),
964 }),
965 Surface::Image(Image {
966 pos: Vec3::new(0.0, 0.0, 0.0),
967 rotation_matrix: Mat3::new(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0),
968 }),
969 ];
970
971 let result = reversed_surface_id(&surfaces, 2);
972 assert_eq!(result, 2);
973
974 let result = reversed_surface_id(&surfaces, 1);
975 assert_eq!(result, 3);
976 }
977}