From d7aaa3db3c6d3083f518936b34847792cfc3190c Mon Sep 17 00:00:00 2001 From: Sebastian Achim Mueller Date: Wed, 6 Dec 2023 17:51:00 +0100 Subject: [PATCH] prepare plenoscope rollout --- .../light_field_camera/__init__.py | 609 +++++++++--------- .../tests/test_creation.py | 148 +++-- .../version.py | 2 +- setup.py | 2 +- 4 files changed, 424 insertions(+), 337 deletions(-) diff --git a/computer_aided_design_for_optical_instruments/light_field_camera/__init__.py b/computer_aided_design_for_optical_instruments/light_field_camera/__init__.py index 4e2445f..347dd29 100644 --- a/computer_aided_design_for_optical_instruments/light_field_camera/__init__.py +++ b/computer_aided_design_for_optical_instruments/light_field_camera/__init__.py @@ -21,199 +21,325 @@ def make_example_config(): } -def make_geometry( - primary_optics_focal_length, - primary_optics_diameter, - camera_field_of_view_polygon, - eye_field_of_view_full_angle, - eyes_point_towards_center_of_primary_optics, - body_overhead, - screen_curvature_radius=1e12, -): +def _init_principal_aperture(focal_length, inner_diameter): """ - primary_optics_focal_length : float - The expected focal-length of the primary optics (probably a mirror). - primary_optics_diameter : float - The expected diameter of the primary optics. - camera_field_of_view_polygon : OrderedDict[(str, [float,float,float]) - In units of rad. The outer perimiter of the field-of-view. Only eyes - which fit into it will be added to the camera. - eye_field_of_view_full_angle : float - In units of rad. - eyes_point_towards_center_of_primary_optics : bool - If true, the camera's screen is curved and better suited for cameras - with larger field-of-views. + Parameters + ---------- + focal_length : float + The focal-length of the principal aperture which is infornt + (which comes before) the light-field camera. In case of the + Cherenkov-plenoscope this is related to the mirror. + inner_diameter : float + The largest diameter of a ring to fit inside the aperture. In case of + a hexagonal mirror, this is the inner diameter of the hexagon. + + Returns + ------- + principal_aperture : dict + Properties of the principal aperture plane w.r.t. the frame of the + light-field camera. """ - assert primary_optics_focal_length > 0.0 - assert primary_optics_diameter > 0.0 - assert len(camera_field_of_view_polygon) >= 3 - assert eye_field_of_view_full_angle > 0.0 - assert body_overhead > 0.0 - + assert focal_length > 0.0 + assert inner_diameter > 0.0 c = {} - c["primary_optics"] = {} - c["primary_optics"]["focal_length"] = float(primary_optics_focal_length) - c["primary_optics"]["diameter"] = float(primary_optics_diameter) - c["primary_optics"]["radius"] = 0.5 * c["primary_optics"]["diameter"] - c["primary_optics"]["fstop"] = ( - c["primary_optics"]["focal_length"] / c["primary_optics"]["diameter"] - ) - c["primary_optics"]["center"] = np.array( - [0.0, 0.0, -c["primary_optics"]["focal_length"]] - ) + c["focal_length"] = float(focal_length) + c["inner_diameter"] = float(inner_diameter) + c["inner_radius"] = 0.5 * c["inner_diameter"] + c["fstop"] = c["focal_length"] / c["inner_diameter"] + c["origin"] = np.array([0.0, 0.0, -c["focal_length"]]) + c["optical_axis"] = np.array([0.0, 0.0, 1.0]) + return c - c["field_of_view"] = {} - _, c["field_of_view"]["polygon"] = optcad.polygon.to_keys_and_numpy_array( - polygon=camera_field_of_view_polygon - ) + +def _init_screen(polygon, eyes_spacing, curvature_radius, eyes_facing_point): + """ + Parameters + ---------- + polygon : dict str -> 3D np.array + Inside this polygon, eyes will be placed. + eyes_spacing : float + The spacing of the eyes in a hexagonal grid. This is the inner diameter + of the hexagons in the grid. + curvature_radius : float + The screen's radius of curvature. + eyes_facing_point : 3D np.array + The position the eyes are facing to. + """ + assert eyes_spacing > 0.0 + eyes_facing_point = np.array(eyes_facing_point) + + c = {} + c["curvature_radius"] = float(curvature_radius) + c["eyes_centers_limit"] = {} + c["eyes_centers_limit"]["polygon"] = polygon ( - c["field_of_view"]["cxlim"], - c["field_of_view"]["cylim"], + c["eyes_centers_limit"]["xlim"], + c["eyes_centers_limit"]["ylim"], _, - ) = optcad.polygon.limits(polygon=camera_field_of_view_polygon) + ) = optcad.polygon.limits(polygon=c["eyes_centers_limit"]["polygon"]) - _cxout = np.max(np.abs(c["field_of_view"]["cxlim"])) - _cyout = np.max(np.abs(c["field_of_view"]["cylim"])) - c["field_of_view"][ - "half_angle_to_safely_include_all_potential_eyes" - ] = 1.2 * np.hypot(_cxout, _cyout) + c["eyes_grid"] = {} + c["eyes_grid"]["spacing"] = eyes_spacing + _xout = np.max(np.abs(c["eyes_centers_limit"]["xlim"])) + _yout = np.max(np.abs(c["eyes_centers_limit"]["ylim"])) + c["eyes_grid"][ + "radius_to_safely_include_all_potential_eyes" + ] = 2 * np.hypot(_xout, _yout) - c["eye"] = {} - c["eye"]["hull"] = {} - c["eye"]["hull"]["field_of_view"] = {} - c["eye"]["hull"]["field_of_view"]["full_angle"] = float( - eye_field_of_view_full_angle + c["eyes_grid"]["fn"] = int( + np.ceil( + c["eyes_grid"]["radius_to_safely_include_all_potential_eyes"] + / c["eyes_grid"]["spacing"] + ) ) - c["eye"]["hull"]["field_of_view"]["half_angle"] = ( - 0.5 * c["eye"]["hull"]["field_of_view"]["full_angle"] + _eyes_centers = optcad.geometry.grid.hexagonal.init_from_spacing( + spacing=c["eyes_grid"]["spacing"], + ref="", + fN=c["eyes_grid"]["fn"], ) - c["eye"]["hull"]["radius"] = ( - np.tan(c["eye"]["hull"]["field_of_view"]["half_angle"]) - * c["primary_optics"]["focal_length"] + alpha = np.deg2rad(30) + sinA = np.sin(alpha) + cosA = np.cos(alpha) + for key in _eyes_centers: + x, y, z = _eyes_centers[key] + npos = np.array([cosA * x - sinA * y, sinA * x + cosA * y, 0.0]) + _eyes_centers[key] = npos + + c["eyes_grid"]["centers"] = optcad.polygon.get_vertices_inside( + vertices=_eyes_centers, + polygon=c["eyes_centers_limit"]["polygon"], ) - c["eye"]["hull"]["diameter"] = 2.0 * c["eye"]["hull"]["radius"] - grid_fn = int( - np.ceil( - c["field_of_view"][ - "half_angle_to_safely_include_all_potential_eyes" - ] - / c["eye"]["hull"]["field_of_view"]["full_angle"] - ) - ) - _anticipated_viewing_directions_of_eyes = ( - optcad.geometry.grid.hexagonal.init_from_spacing( - spacing=c["eye"]["hull"]["field_of_view"]["full_angle"], - ref="", - fN=grid_fn, + _eyes_voronoi_cells = ( + optcad.geometry.grid.hexagonal.init_voronoi_cells_from_centers( + centers=c["eyes_grid"]["centers"], + centers_spacing=c["eyes_grid"]["spacing"], + rot=alpha, ) ) - anticipated_viewing_directions_of_eyes = ( - optcad.polygon.get_vertices_inside( - vertices=_anticipated_viewing_directions_of_eyes, - polygon=camera_field_of_view_polygon, - ) + c["hull"] = {} + c["hull"][ + "polygon" + ] = optcad.geometry.grid.hexagonal.find_hull_of_voronoi_cells( + voronoi_cells=_eyes_voronoi_cells, + centers=c["eyes_grid"]["centers"], + centers_spacing=c["eyes_grid"]["spacing"], ) - c["screen"] = {} - c["screen"]["curvature_radius"] = float(screen_curvature_radius) - c["screen"]["eyes"] = [] - - for ci, ckey in enumerate(anticipated_viewing_directions_of_eyes): + c["eyes"] = [] + for conid, hexid in enumerate(c["eyes_grid"]["centers"]): eye = {} - eye["continuous_eye_id"] = ci - eye["hexagonal_grid_id"] = ckey + eye["continuous_eye_id"] = conid + eye["hexagonal_grid_id"] = hexid - eye["pos"] = ( - c["primary_optics"]["focal_length"] - * anticipated_viewing_directions_of_eyes[ckey] - ) - _sphere_z = optcad.geometry.sphere.surface_height( + eye["pos"] = c["eyes_grid"]["centers"][hexid] + + eye["pos"][2] = optcad.geometry.sphere.surface_height( x=eye["pos"][0], y=eye["pos"][1], - curvature_radius=c["screen"]["curvature_radius"], + curvature_radius=c["curvature_radius"], ) - eye["pos"][2] = -1.0 * _sphere_z - - if eyes_point_towards_center_of_primary_optics: - _axis, _angle = segmented_mirror.facet_rotation_axis_and_angle( - facet_center=eye["pos"], - target_point=c["primary_optics"]["center"], - direction_incoming_light=np.array([0.0, 0.0, 1.0]), - ) - if np.abs(_angle) > 0.0: - eye["rot"] = { - "repr": "axis_angle", - "axis": _axis, - "angle_deg": float(np.rad2deg(_angle)), - } - else: - eye["rot"] = {"repr": "tait_bryan", "xyz_deg": [0, 0, 0]} + + _axis, _angle = segmented_mirror.facet_rotation_axis_and_angle( + facet_center=eye["pos"], + target_point=eyes_facing_point, + direction_incoming_light=np.array([0.0, 0.0, 1.0]), + ) + if np.abs(_angle) > 0.0: + eye["rot"] = { + "repr": "axis_angle", + "axis": _axis, + "angle_deg": np.rad2deg(_angle), + } else: eye["rot"] = {"repr": "tait_bryan", "xyz_deg": [0, 0, 0]} - c["screen"]["eyes"].append(eye) + c["eyes"].append(eye) - eyes_positions = {} - for i in range(len(c["screen"]["eyes"])): - vkey = c["screen"]["eyes"][i]["hexagonal_grid_id"] - eyes_positions[vkey] = c["screen"]["eyes"][i]["pos"] + return c - _screen_eyes_positions_voronoi_cells = ( - optcad.geometry.grid.hexagonal.init_voronoi_cells_from_centers( - centers=eyes_positions, - centers_spacing=c["eye"]["hull"]["diameter"], - rot=0.0, - ) - ) - c["screen"][ - "polygon" - ] = optcad.geometry.grid.hexagonal.find_hull_of_voronoi_cells( - voronoi_cells=_screen_eyes_positions_voronoi_cells, - centers=eyes_positions, - centers_spacing=c["eye"]["hull"]["diameter"], +def _init_housing( + screen_eyes_centers_limit_polygon, + screen_hull_polygon, + body_overhead_radius, + curvature_radius, + vs_hex_grid, + min_hull_fn=36, +): + assert body_overhead_radius >= 0.0 + assert min_hull_fn >= 3 + + c = {} + c["hull"] = {} + c["hull"]["minkowski_regular_polygon_fn"] = max( + [min_hull_fn, len(screen_hull_polygon)] ) - fovpoly = {} - for vkey in camera_field_of_view_polygon: - fovpoly[vkey] = ( - c["primary_optics"]["focal_length"] - * camera_field_of_view_polygon[vkey] - ) + _circle = optcad.geometry.regular_polygon.make_vertices_xy( + outer_radius=body_overhead_radius, + fn=c["hull"]["minkowski_regular_polygon_fn"], + ) - c["body"] = {} - c["body"]["polygon"] = optcad.minkowski.minkowski_hull_xy( - poly1=fovpoly, - poly2=optcad.geometry.regular_polygon.make_vertices_xy( - outer_radius=body_overhead, - fn=max([24, len(camera_field_of_view_polygon)]), - ), + c["hull"]["polygon"] = optcad.minkowski.minkowski_hull_xy( + poly1=screen_eyes_centers_limit_polygon, + poly2=_circle, ref="body/outer_bound", ) - c["body"]["mesh"] = optcad.primitives.template_curved_surface.init( - outer_polygon=c["body"]["polygon"], - curvature_config={ - "curvature_radius": c["primary_optics"]["focal_length"] - }, + c["hull"]["mesh"] = optcad.primitives.template_curved_surface.init( + outer_polygon=c["hull"]["polygon"], + curvature_config={"curvature_radius": curvature_radius}, curvature_height_function=optcad.geometry.sphere.surface_height, curvature_surface_normal_function=optcad.geometry.sphere.surface_normal, - inner_polygon=c["screen"]["polygon"], - fn_hex_grid=3 * grid_fn, - ref="body", + inner_polygon=screen_hull_polygon, + vs_hex_grid=vs_hex_grid, + fn_hex_grid=None, + ref="housing", + ) + return c + + +def _init_eye( + eyes_spacing, + lens_fill_factor, + photosensor_fill_factor, + lens_curvature_radius, + photosensor_num_on_diagonal, +): + c = {} + c["geometry"] = optcad.primitives.light_field_eye.make_geometry( + housing_outer_radius=0.05, + housing_wall_width=2e-3, + housing_height=0.01, + lens_curvature_radius=lens_curvature_radius, + lens_fn=3, + photosensor_num_on_diagonal=photosensor_num_on_diagonal, + photosensor_gap=0.0e-3, + photosensor_plane_distance=0.12, + ) + return c + + +def make_geometry( + principal_aperture_focal_length, + principal_aperture_diameter, + screen_polygon, + screen_curvature_radius, + eyes_spacing, + eyes_facing_point, + eyes_photosensor_num_on_diagonal, + eyes_photosensor_gap, + eyes_lens_curvature_radius, + eyes_lens_gap, + eyes_lens_fn, + eyes_housing_wall_width, + body_overhead_radius, +): + """ + principal_aperture_focal_length : float + The expected focal-length of the primary optics (probably a mirror). + principal_aperture_diameter : float + The expected diameter of the primary optics. + screen_polygon : OrderedDict[(str, [float,float,float]) + The outer perimeter of the screen. Only eyes which fit into it will + be added to the camera. + screen_curvature_radius : float + The screen's radius of curvature. + eyes_spacing : float + Spacing of eyes in the x-y plane of the screen. + eyes_facing_point : 3D np.array + Eyes will face to this point. It effects the orientation of the eyes. + eyes_lens_gap : float + Gap in between lenses and housings of neighboring eyes. + eyes_photosensor_num_on_diagonal: + This many photosensor will be on the diagonal of an individual eye. + eyes_photosensor_gap : float + Gap in between neighboring photosensors inside the eye. + eyes_housing_wall_width : float + Width of the wall surrounding each eye. + eyes_lens_curvature_radius : float + Must match the lens' refractive index. + body_overhead_radius : float + Radius. + """ + assert principal_aperture_focal_length > 0.0 + assert principal_aperture_diameter > 0.0 + assert len(screen_polygon) >= 3 + assert eyes_spacing > 0.0 + assert body_overhead_radius > 0.0 + HEXIN2OUT = 2.0 / np.sqrt(3.0) + HEXOUT2IN = 1.0 / HEXIN2OUT + + c = {} + c["principal_aperture"] = _init_principal_aperture( + focal_length=principal_aperture_focal_length, + inner_diameter=principal_aperture_diameter, + ) + + c["screen"] = _init_screen( + polygon=screen_polygon, + eyes_spacing=eyes_spacing, + curvature_radius=screen_curvature_radius, + eyes_facing_point=eyes_facing_point, + ) + + c["housing"] = _init_housing( + screen_eyes_centers_limit_polygon=c["screen"]["eyes_centers_limit"][ + "polygon" + ], + screen_hull_polygon=c["screen"]["hull"]["polygon"], + body_overhead_radius=body_overhead_radius, + curvature_radius=screen_curvature_radius, + vs_hex_grid=eyes_spacing / 2.0, + min_hull_fn=36, + ) + + assert eyes_spacing > eyes_lens_gap + eyes_housing_inner_radius = 0.5 * (eyes_spacing - eyes_lens_gap) + eyes_housing_outer_radius = eyes_housing_inner_radius * HEXIN2OUT + assert eyes_lens_curvature_radius >= eyes_housing_outer_radius + + eyes_fstop = c["principal_aperture"]["fstop"] + eyes_photosensor_plane_distance = eyes_housing_inner_radius * eyes_fstop + eyes_housing_height = 0.5 * eyes_photosensor_plane_distance + + c["eye"] = {} + c["eye"]["geometry"] = optcad.primitives.light_field_eye.make_geometry( + housing_outer_radius=eyes_housing_outer_radius, + housing_wall_width=eyes_housing_wall_width, + housing_height=eyes_housing_height, + lens_curvature_radius=eyes_lens_curvature_radius, + lens_fn=eyes_lens_fn, + photosensor_num_on_diagonal=eyes_photosensor_num_on_diagonal, + photosensor_gap=eyes_photosensor_gap, + photosensor_plane_distance=eyes_photosensor_plane_distance, + ) + + c["eye"]["mesh"] = optcad.primitives.light_field_eye.init( + geometry=c["eye"]["geometry"], + ref="eye", ) return c +def _assert_int(a): + assert a % 1.0 == 0.0 + return int(a) + + def add_to_frame( frame, light_field_camera_geometry, - boundary_layer_eye_lens_working="eye/lens/working", - boundary_layer_eye_lens_body="eye/lens/body", + boundary_layer_eye_lens="eye/lens", + boundary_layer_eye_housing_inner="eye/housing/inner", + boundary_layer_eye_housing_outer="eye/housing/outer", boundary_layer_eye_photosesnsor="eye/photosensor", + boundary_layer_housing="housing", + continuous_eye_id_offset=1000 * 1000 * 1000, + housing_id_offset=1000, ref="light_field_camera", ): """ @@ -226,155 +352,62 @@ def add_to_frame( The light-field camera's geometry. A dict with all the relevant positions and orientations pre calculated. """ - objs = {} - - return objs - + continuous_eye_id_offset = _assert_int(continuous_eye_id_offset) + housing_id_offset = _assert_int(housing_id_offset) -""" - -def add_segmented_mirror_to_frame_in_scenery( - frame, - scenery, - - camera_field_of_view, - outer_medium="vacuum", - camera_num_photosensors_on_diagonal=5, - cameras_point_towards_center_of_primary_optics=True, - camera_surface_mirror="perfect_mirror", - camera_surface_body="perfect_absorber", - camera_photosensor_surface="perfect_absorber/rgb_12_12_12", - camera_photosensor_gap=1e-3, - camera_lens_medium="glass", - camera_lens_fn=7, - ref="light_field_sensor", -): + c = light_field_camera_geometry + objs = {} - Parameters - ---------- - frame : dict - A frame in the scenery. - scenery : dict - The scenery. - config : dict - The geometry of the working-surface in Sebastian's format used since - his Master-thesis. - - ref : str - A name to distinguish this mirror from others. - join = posixpath.join - - - # camera - # ------ - camera_ref = join(ref, "camera") - camera_housing_inner_radius = ( - camera_field_of_view * primary_optics_focal_length - ) - camera_housing_outer_radius = (2 / np.sqrt(3)) * camera_housing_inner_radius + # eyes + # ---- + eye_obj_key = posixpath.join(ref, "eye") + objs[eye_obj_key] = optcad.export.reduce_mesh_to_obj(c["eye"]["mesh"]) - expected_primary_optics_fstop_number = ( - primary_optics_focal_length - / primary_optics_diameter - ) + eye_mtl = {} + for mtl in c["eye"]["mesh"]["materials"]: + eye_mtl[mtl] = boundary_layer_eye_photosesnsor - camera_geometry = LightFieldCameraModule.make_geometry( - housing_outer_radius=camera_housing_outer_radius, - housing_wall_width=, - housing_height=, - lens_curvature_radius=, - lens_fn=camera_lens_fn, - photosensor_num_on_diagonal=, - photosensor_gap=, - photosensor_plane_distance=, - ) - camera_mesh = LightFieldCameraModule.init( - camera_geometry=camera_geometry, - ref=camera_ref, - ) - camera_mtl = { - join(ref, "cam", "lens", "top"): join(ref, "lens"), - join(ref, "cam", "lens", "bottom"): join(ref + "l"), - join(ref, "cam", "lens", "side"): join(ref + "h"), - join(ref, "cam", "housing", "top"): join(ref + "h"), - join(ref, "cam", "housing", "bottom"): join(ref + "h"), - join(ref, "cam", "housing", "outer"): join(ref + "h"), - join(ref, "cam", "housing", "inner"): join(ref + "m"), - } - num_photosensors = len( - camera_geometry["photosensor"]["grid"]["positions"] - ) - for i in range(num_photosensors): - mtlkey = join(ref, cam, "photosensor_{:06d}".format(i)) - camera_mtl[mtlkey] = ref + "p" - - # add objects - # ----------- - assert camera_ref not in scenery["objects"] - scenery["objects"][camera_ref] = camera_mesh - - # facet-supports - # -------------- - approx_num_facets_on_outer_radius = ( - config["max_outer_aperture_radius"] / config["facet_inner_hex_radius"] - ) + eye_mtl["eye/lens/top"] = boundary_layer_eye_lens + eye_mtl["eye/lens/side"] = boundary_layer_eye_housing_outer + eye_mtl["eye/lens/bot"] = boundary_layer_eye_lens - fn_circle = int(np.ceil(2.0 * np.pi * approx_num_facets_on_outer_radius)) - grid_spacing = ( - 2.0 * config["facet_inner_hex_radius"] + config["gap_between_facets"] - ) + eye_mtl["eye/housing/inner"] = boundary_layer_eye_housing_inner + eye_mtl["eye/housing/outer"] = boundary_layer_eye_housing_outer + eye_mtl["eye/housing/bottom"] = boundary_layer_eye_housing_outer + eye_mtl["eye/housing/top"] = boundary_layer_eye_housing_outer - # outer bound xy - # -------------- - outer_radius_facet_supports = ( - config["max_outer_aperture_radius"] - config["facet_inner_hex_radius"] - ) - inner_radius_facet_supports = ( - config["min_inner_aperture_radius"] + config["facet_inner_hex_radius"] - ) + for mtl in eye_mtl: + eye_mtl[mtl] = posixpath.join(ref, eye_mtl[mtl]) - aperture_outer_polygon = optcad.geometry.regular_polygon.make_vertices_xy( - outer_radius=outer_radius_facet_supports, fn=fn_circle, rot=0.0, - ) + for i in range(len(c["screen"]["eyes"])): + eye = c["screen"]["eyes"][i] + child = { + "id": continuous_eye_id_offset + eye["continuous_eye_id"], + "pos": eye["pos"], + "rot": eye["rot"], + "obj": eye_obj_key, + "mtl": eye_mtl, + } + frame["children"].append(child) - facet_centers = init_facet_centers_xy( - aperture_outer_polygon=aperture_outer_polygon, - aperture_inner_polygon=optcad.geometry.regular_polygon.make_vertices_xy( - outer_radius=inner_radius_facet_supports, fn=fn_circle, - ), - grid_spacing=grid_spacing, - grid_style="hexagonal", - grid_rotation=np.pi / 2, - center_of_grid=[0.0, 0.0], - ref="facet_centers", + # body + # ---- + housing_obj_key = posixpath.join(ref, "housing") + objs[housing_obj_key] = optcad.export.reduce_mesh_to_obj( + c["housing"]["hull"]["mesh"] ) - facet_centers = set_facet_centers_z( - facet_centers=facet_centers, - focal_length=config["focal_length"], - DaviesCotton_over_parabolic_mixing_factor=config[ - "DaviesCotton_over_parabolic_mixing_factor" - ], - max_delta=1e-6 * config["focal_length"], - max_iterations=1000, - ) + housing_mtl = {"housing": boundary_layer_housing} + for mtl in housing_mtl: + housing_mtl[mtl] = posixpath.join(ref, housing_mtl[mtl]) - # orientation - # ----------- - focal_point = [0, 0, config["focal_length"]] - camera_id = 0 - for fkey in camera_centers: - child = { - "id": int(camera_id), - "pos": camera_centers[fkey], - "rot": init_camera_rotation( - camera_centers=camera_centers[fkey], focal_point=focal_point, - ), - "obj": camera_ref, - "mtl": camera_mtl_to_boundary_layers_map, - } - frame["children"].append(child) - camera_id += 1 + child = { + "id": housing_id_offset + 0, + "pos": [0, 0, 0], + "rot": {"repr": "tait_bryan", "xyz_deg": [0, 0, 0]}, + "obj": housing_obj_key, + "mtl": housing_mtl, + } + frame["children"].append(child) - return scenery -""" + return objs diff --git a/computer_aided_design_for_optical_instruments/tests/test_creation.py b/computer_aided_design_for_optical_instruments/tests/test_creation.py index 35aa53f..39a6828 100644 --- a/computer_aided_design_for_optical_instruments/tests/test_creation.py +++ b/computer_aided_design_for_optical_instruments/tests/test_creation.py @@ -12,6 +12,8 @@ import sebastians_matplotlib_addons as sebplt +FOCAL_LENGTH = 106.5 + scenery = merlict.scenery.init(default_medium="vacuum") frame = { @@ -25,28 +27,28 @@ aperture_outer_polygon = optcad.geometry.regular_polygon.make_vertices_xy( - outer_radius=355e-3, + outer_radius=0.5 * (FOCAL_LENGTH / 1.5) * (np.sqrt(3.0) / 2.0), fn=6, rot=np.pi / 6, ) mirror_objs = cadoptins.segmented_mirror.add_to_frame( frame=frame, - focal_length=2000e-3, + focal_length=FOCAL_LENGTH, aperture_outer_polygon=aperture_outer_polygon, aperture_inner_polygon=None, - facet_inner_hex_radius=54e-3, - gap_between_facets=4e-3, - facet_body_width=15e-3, + facet_inner_hex_radius=800e-3, + gap_between_facets=20e-3, + facet_body_width=25e-3, davies_cotton_weight=0.0, - parabola_weight=0.0, - sphere_weight=1.0, + parabola_weight=1.0, + sphere_weight=0.0, facet_rotation="sphere", mean_distance_of_facet_centers_to_focal_point_is_focal_length=False, boundary_layer_facet_front="facet/front", boundary_layer_facet_body="facet/body", ref="mirror", - facet_fn=3, + facet_fn=6, ) scenery["geometry"]["objects"].update(mirror_objs) @@ -68,59 +70,45 @@ merlict.scenery.write_tar(sceneryPy=scenery, path="segmir.tar") - -camera_geometry = optcad.primitives.light_field_eye.make_geometry( - housing_outer_radius=0.05, - housing_wall_width=2e-3, - housing_height=0.01, - lens_curvature_radius=0.125, - lens_fn=3, - photosensor_num_on_diagonal=9, - photosensor_gap=0.0e-3, - photosensor_plane_distance=0.12, -) -camera = optcad.primitives.light_field_eye.init( - camera_geometry=camera_geometry, - ref="cam", -) -camera_obj = optcad.export.reduce_mesh_to_obj(camera) - -with open("eye.obj", "wt") as f: - f.write(triangle_mesh_io.obj.dumps(camera_obj)) - - -field_of_view_polygon = optcad.geometry.regular_polygon.make_vertices_xy( - outer_radius=np.deg2rad(2.5 / 2 - 0.06667 / 2), +screen_polygon = optcad.geometry.regular_polygon.make_vertices_xy( + outer_radius=FOCAL_LENGTH * np.deg2rad(6.5 / 2 - 0.06667 / 2), fn=5, ) -lfs = cadoptins.light_field_camera.make_geometry( - primary_optics_focal_length=106.5, - primary_optics_diameter=71, - camera_field_of_view_polygon=field_of_view_polygon, - eye_field_of_view_full_angle=np.deg2rad(0.06667), - eyes_point_towards_center_of_primary_optics=True, - body_overhead=0.25, +lfc_geometry = cadoptins.light_field_camera.make_geometry( + principal_aperture_focal_length=FOCAL_LENGTH, + principal_aperture_diameter=71, + screen_polygon=screen_polygon, + screen_curvature_radius=-FOCAL_LENGTH, + eyes_spacing=FOCAL_LENGTH * np.deg2rad(0.0667), + eyes_facing_point=[0, 0, -FOCAL_LENGTH], + eyes_photosensor_num_on_diagonal=5, + eyes_photosensor_gap=1e-3, + eyes_lens_curvature_radius=1.0, + eyes_lens_gap=2e-3, + eyes_lens_fn=7, + eyes_housing_wall_width=2e-3, + body_overhead_radius=0.33, ) fig = sebplt.figure(style={"rows": 1440, "cols": 2560, "fontsize": 1}) ax = sebplt.add_axes(fig=fig, span=[0.1, 0.1, 0.8, 0.8]) -optcad.plot.ax_add_mesh_xy(ax=ax, mesh=lfs["body"]["mesh"]) +optcad.plot.ax_add_mesh_xy(ax=ax, mesh=lfc_geometry["housing"]["hull"]["mesh"]) optcad.plot.ax_add_polygon( ax=ax, - polygon=lfs["screen"]["polygon"], + polygon=lfc_geometry["screen"]["hull"]["polygon"], closed=True, linestyle="-", color="r", alpha=0.3, ) ax.set_aspect("equal") -fig.savefig("body.png") +fig.savefig("housing.png") sebplt.close(fig) -with open("body.obj", "wt") as f: - mesh = lfs["body"]["mesh"] +with open("housing.obj", "wt") as f: + mesh = lfc_geometry["housing"]["hull"]["mesh"] mesh = optcad.mesh.remove_unused_vertices_and_vertex_normals(mesh) f.write(triangle_mesh_io.obj.dumps(optcad.export.reduce_mesh_to_obj(mesh))) @@ -128,7 +116,7 @@ fig = sebplt.figure() ax = sebplt.add_axes(fig=fig, span=[0.1, 0.1, 0.8, 0.8]) -for eye in lfs["screen"]["eyes"]: +for eye in lfc_geometry["screen"]["eyes"]: ax.plot( eye["pos"][0], eye["pos"][1], @@ -137,7 +125,7 @@ optcad.plot.ax_add_polygon( ax=ax, - polygon=lfs["screen"]["polygon"], + polygon=lfc_geometry["screen"]["hull"]["polygon"], closed=True, linestyle="-", color="r", @@ -147,10 +135,76 @@ fig.savefig("face_verts.jpg") sebplt.close(fig) + lfc_frame = { - "id": 133742, - "pos": [0, 0, 5], + "id": 1337, + "pos": [0, 0, FOCAL_LENGTH], "rot": {"repr": "tait_bryan", "xyz_deg": [0, 0, 0]}, "children": [], } scenery["geometry"]["relations"]["children"].append(lfc_frame) + +lfc_objs = cadoptins.light_field_camera.add_to_frame( + frame=lfc_frame, + light_field_camera_geometry=lfc_geometry, +) +scenery["geometry"]["objects"].update(lfc_objs) + + +scenery["materials"]["surfaces"]["glass"] = { + "material": "transparent", + "specular_reflection": [[200e-9, 0.0], [1.2e-6, 0.0]], + "diffuse_reflection": [[200e-9, 0.0], [1.2e-6, 0.0]], + "color": [200, 200, 200], +} +scenery["materials"]["media"]["schott_n_kzfs2"] = { + "refraction": [ + [334.1e-9, 1.59259], + [365.0e-9, 1.58382], + [404.7e-9, 1.57580], + [435.8e-9, 1.57114], + [480.0e-9, 1.56612], + [486.1e-9, 1.56553], + [587.6e-9, 1.55836], + [589.3e-9, 1.55827], + [632.8e-9, 1.55617], + [643.8e-9, 1.55570], + [656.3e-9, 1.55519], + [706.5e-9, 1.55337], + [852.1e-9, 1.54944], + [1014.0e-9, 1.54625], + [1060.0e-9, 1.54546], + [1529.6e-9, 1.53798], + [1970.1e-9, 1.53011], + [2325.4e-9, 1.52239], + ], + "absorbtion": [[200e-9, 1e99], [1.2e-6, 1e99]], +} + +scenery["materials"]["boundary_layers"]["light_field_camera/eye/lens"] = { + "inner": {"medium": "schott_n_kzfs2", "surface": "glass"}, + "outer": {"medium": "vacuum", "surface": "glass"}, +} +scenery["materials"]["boundary_layers"][ + "light_field_camera/eye/housing/inner" +] = { + "inner": {"medium": "vacuum", "surface": "perfect_mirror"}, + "outer": {"medium": "vacuum", "surface": "perfect_absorber"}, +} +scenery["materials"]["boundary_layers"][ + "light_field_camera/eye/housing/outer" +] = { + "inner": {"medium": "vacuum", "surface": "perfect_absorber"}, + "outer": {"medium": "vacuum", "surface": "perfect_absorber"}, +} +scenery["materials"]["boundary_layers"][ + "light_field_camera/eye/photosensor" +] = { + "inner": {"medium": "vacuum", "surface": "perfect_absorber"}, + "outer": {"medium": "vacuum", "surface": "perfect_absorber"}, +} +scenery["materials"]["boundary_layers"]["light_field_camera/housing"] = { + "inner": {"medium": "vacuum", "surface": "perfect_absorber"}, + "outer": {"medium": "vacuum", "surface": "perfect_absorber"}, +} +merlict.scenery.write_tar(sceneryPy=scenery, path="plenoscope.tar") diff --git a/computer_aided_design_for_optical_instruments/version.py b/computer_aided_design_for_optical_instruments/version.py index f102a9c..3b93d0b 100644 --- a/computer_aided_design_for_optical_instruments/version.py +++ b/computer_aided_design_for_optical_instruments/version.py @@ -1 +1 @@ -__version__ = "0.0.1" +__version__ = "0.0.2" diff --git a/setup.py b/setup.py index 2f72d42..824444d 100644 --- a/setup.py +++ b/setup.py @@ -31,7 +31,7 @@ ], package_data={"computer_aided_design_for_optical_instruments": []}, install_requires=[ - "optic_object_wavefronts", + "optic_object_wavefronts>=1.3.7", "merlict", ], classifiers=[