use std::sync::Arc;
use sophus_autodiff::linalg::VecF64;
use sophus_geo::Circle;
use sophus_image::{
ImageSize,
IsIntensityArcImage,
MutImageF32,
};
use log::warn;
use sophus_lie::Isometry3F64;
use sophus_opt::{
nlls::{
CostFn,
CostTerms,
OptParams,
optimize_nlls,
},
prelude::{
IsImageView,
IsMutImageView,
},
variables::{
VarBuilder,
VarFamily,
VarKind,
},
};
use sophus_renderer::{
camera::{
RenderCamera,
RenderCameraProperties,
},
renderables::{
Color,
ImageFrame,
},
};
use sophus_viewer::packets::{
ImageViewPacket,
Packet,
append_to_scene_packet,
create_scene_packet,
delete_image_packet,
delete_scene_packet,
};
use thingbuf::mpsc::blocking::Sender;
use crate::examples::optics_sim::{
aperture_stop::ApertureStop,
convex_lens::{
BiConvexLens2,
BiConvexLens2F64,
},
cost::ChiefRayCost,
detector::Detector,
element::Element,
light_path::LightPath,
scene_point::ScenePoints,
};
pub mod aperture_stop;
pub mod convex_lens;
pub mod cost;
pub mod detector;
pub mod element;
pub mod light_path;
pub mod scene_point;
pub fn refine_chief_ray_angle(
angle: f64,
scene_point: VecF64<2>,
lens: Arc<BiConvexLens2F64>,
target_point: VecF64<2>,
) -> f64 {
let family: VarFamily<VecF64<1>> = VarFamily::new(VarKind::Free, vec![VecF64::<1>::new(angle)]);
let variables = VarBuilder::new().add_family("angle", family).build();
let mut chief_ray_cost = CostTerms::new(["angle"], vec![]);
chief_ray_cost.collection.push(ChiefRayCost {
scene_point,
aperture: target_point,
entity_indices: [0],
});
let solution = optimize_nlls(
variables,
vec![CostFn::new_boxed(lens, chief_ray_cost)],
OptParams {
num_iterations: 100,
initial_lm_damping: 1.0,
parallelize: true,
solver: Default::default(),
},
)
.unwrap();
solution.variables.get_members::<VecF64<1>>("angle")[0][0]
}
pub struct OpticsElements {
pub lens: Arc<BiConvexLens2<f64, 0, 0>>,
pub detector: Detector,
pub aperture: ApertureStop,
pub scene_points: ScenePoints,
}
impl Default for OpticsElements {
fn default() -> Self {
Self::new()
}
}
impl OpticsElements {
pub fn new() -> Self {
OpticsElements {
detector: Detector { x: 0.5 },
lens: Arc::new(BiConvexLens2::new(
Circle {
center: VecF64::<2>::new(0.560, 0.0),
radius: 0.600,
},
Circle {
center: VecF64::<2>::new(-0.560, 0.0),
radius: 0.600,
},
1.5,
)),
scene_points: ScenePoints {
p: [VecF64::<2>::new(-2.5, 0.1), VecF64::<2>::new(-2.0, -0.1)],
},
aperture: ApertureStop {
x: 0.2,
radius: 0.02,
},
}
}
}
pub struct OpticsSimWidget {
pub elements: OpticsElements,
message_send: Sender<Vec<Packet>>,
}
impl Drop for OpticsSimWidget {
fn drop(&mut self) {
match self.message_send
.send(vec![
delete_scene_packet("scene"),
delete_image_packet("image"),
]){
Ok(_) => {},
Err(_) => {
warn!("Failed to send delete packets, viewer might not be running.");
}
}
}
}
impl OpticsSimWidget {
pub fn new(
message_send: Sender<std::vec::Vec<sophus_viewer::packets::Packet>>,
) -> OpticsSimWidget {
let packets = vec![create_scene_packet(
"scene",
RenderCamera {
scene_from_camera: Isometry3F64::from_translation(VecF64::<3>::new(
-0.5, 0.0, -3.0,
)),
properties: RenderCameraProperties::default_from(ImageSize {
width: 640,
height: 480,
}),
},
true,
)];
message_send.send(packets).unwrap();
OpticsSimWidget {
elements: OpticsElements::new(),
message_send,
}
}
pub fn send_update(&mut self) {
let mut light_path = vec![];
let mut image = MutImageF32::from_image_size(ImageSize {
width: 30,
height: 50,
});
for i in 0..2 {
let top_angle = refine_chief_ray_angle(
0.0,
self.elements.scene_points.p[i],
self.elements.lens.clone(),
VecF64::<2>::new(self.elements.aperture.x, self.elements.aperture.radius),
);
let center_angle = refine_chief_ray_angle(
0.0,
self.elements.scene_points.p[i],
self.elements.lens.clone(),
VecF64::<2>::new(self.elements.aperture.x, 0.0),
);
let bottom_angle = refine_chief_ray_angle(
0.0,
self.elements.scene_points.p[i],
self.elements.lens.clone(),
VecF64::<2>::new(self.elements.aperture.x, -self.elements.aperture.radius),
);
for angle in [
("top", top_angle),
("center", center_angle),
("bottom", bottom_angle),
] {
light_path.push(LightPath::from(
format!("path_{}_{}", i, angle.0),
self.elements.scene_points.p[i],
angle.1,
self.elements.detector.clone(),
self.elements.aperture.clone(),
angle.0 == "center",
if i == 0 { Color::red() } else { Color::blue() },
));
}
let angle_range = top_angle - bottom_angle;
for j in 0..100 {
let angle = bottom_angle + j as f64 * 0.01 * angle_range;
let mut path = LightPath::from(
format!("path_{i}_{j}"),
self.elements.scene_points.p[i],
angle,
self.elements.detector.clone(),
self.elements.aperture.clone(),
false,
if i == 0 { Color::red() } else { Color::blue() },
);
path.propagate(&self.elements.lens);
if let Some(point) = path.image_point {
let pixel = (point * 90.0 + 25.0).round() as usize;
if (0..image.image_size().height).contains(&pixel) {
*image.mut_pixel(0, pixel) += 0.1 * angle_range as f32;
}
}
}
}
for path in light_path.iter_mut() {
path.propagate(&self.elements.lens);
}
let mut renderables3d = vec![
self.elements.detector.to_renderable3(),
self.elements.scene_points.to_renderable3(),
self.elements.lens.to_renderable3(),
self.elements.aperture.to_renderable3(),
];
for path in light_path {
renderables3d.push(path.to_renderable3());
}
let packets = vec![
append_to_scene_packet("scene", renderables3d),
Packet::Image(ImageViewPacket {
view_label: "image".to_owned(),
frame: Some(ImageFrame::from_image(&image.to_shared().to_rgba())),
scene_renderables: vec![],
pixel_renderables: vec![],
delete: false,
}),
];
self.message_send.send(packets).unwrap();
}
}