Source code for rayoptics.gui.appcmds

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright © 2018 Michael J. Hayford
""" generic ray optics commands for creating plots and tables

.. Created on Thu Nov  8 21:21:57 2018

.. codeauthor: Michael J. Hayford
"""

import logging
import math
import pathlib

from opticalglass import glassmap as gm
from opticalglass import glassfactory as gfact
from opticalglass import opticalmedium as om

from rayoptics.codev import cmdproc
from rayoptics.optical import obench
from rayoptics.zemax import zmxread

import rayoptics.optical.opticalmodel as opticalmodel
from rayoptics.elem.profiles import Spherical, Conic
import rayoptics.elem.elements as ele
import rayoptics.elem.parttree as pt
from rayoptics.elem import layout
from rayoptics.parax import diagram
from rayoptics.parax.firstorder import specsheet_from_parax_data
from rayoptics.parax.idealimager import ideal_imager_setup
from rayoptics.parax.etendue import create_etendue_dict
from rayoptics.parax.specsheet import (conjugate_types, SpecSheet,
                                       create_specsheet, create_specsheets,
                                       create_specsheet_from_model)
from rayoptics.raytr import vigcalc
from rayoptics.raytr import trace

from rayoptics.gui.appmanager import ModelInfo
from rayoptics.gui.roafile import open_roa

from rayoptics.mpl.interactivelayout import InteractiveLayout
from rayoptics.mpl.axisarrayfigure import Fit
from rayoptics.mpl.axisarrayfigure import (RayFanFigure, SpotDiagramFigure,
                                           WavefrontFigure)

from rayoptics.mpl.analysisplots import FieldCurveFigure, ThirdOrderBarChart
import rayoptics.mpl.interactivediagram as dgm

import rayoptics.qtgui.plotview as plotview
from rayoptics.qtgui.idealimagerdialog import IdealImagerDialog
from rayoptics.qtgui.pytablemodel import PyTableModel
from rayoptics.qtgui.plotview import (create_plot_scale_panel,
                                      create_multi_plot_scale_panel,
                                      create_draw_rays_groupbox,
                                      create_diagram_controls_groupbox,
                                      create_diagram_edge_actions_groupbox,
                                      create_diagram_layers_groupbox,
                                      create_2d_figure_toolbar)

logger = logging.getLogger(__name__)


[docs]def open_model(file_url, info=False, post_process_imports=True, **kwargs): """ open a file or url and populate an optical model with the data Args: file_url (str): a filename or url of a supported file type - .roa - a rayoptics JSON encoded file - .seq - a CODE V (TM) sequence file - .zmx - a Zemax (TM) lens file - a URL from the www.photonstophotos.net OpticalBench database info (bool): if true, return an info tuple with import statistics post_process_imports (bool): for lens design program file import, kwargs (dict): keyword args passed to the reader functions Returns: if successful, an OpticalModel instance, otherwise, None """ file_url_pth = pathlib.Path(file_url) file_extension = file_url_pth.suffix.lower() opm = None if file_extension == '.roa': # if we have a rayoptics file, we just read it opm = open_roa(file_url_pth, **kwargs) else: # if we're importing another program's file, collect import info if len(file_url_pth.parts) > 0 and file_url_pth.parts[1] == 'www.photonstophotos.net': opm, import_info = obench.read_obench_url(file_url, **kwargs) elif file_extension == '.seq': opm, import_info = cmdproc.read_lens(file_url_pth, **kwargs) elif file_extension == '.zmx': opm, import_info = zmxread.read_lens_file(file_url_pth, **kwargs) # At this point we have seq_model, opticalspec and sys_model. # Generate the remaining databases and relations unless declined. if post_process_imports: create_specsheet_from_model(opm) # create element model and part_tree opm.ele_model.reset_serial_numbers() pt.elements_from_sequence(opm.ele_model, opm.seq_model, opm.part_tree) if info: return opm, import_info return opm
[docs]def create_new_model(): return create_new_optical_system()
[docs]def create_new_optical_system(efl=10.0, epd=1, fov=1.0): imager_inputs = {'s': -math.inf, 'f': efl} inf_conj_imgr = ideal_imager_setup(**imager_inputs) ei = create_etendue_dict() ei['field']['object']['angle'] = fov ei['aperture']['object']['pupil'] = epd ssi = SpecSheet('infinite', imager=inf_conj_imgr, imager_inputs=imager_inputs, etendue_inputs=ei) opt_model = create_new_optical_model_from_specsheet(ssi) sr = opt_model.optical_spec.spectral_region sr.set_from_list([('F', 1), ('d', 1), ('C', 1)]) sr.reference_wvl = 1 return opt_model
[docs]def create_new_optical_model_from_specsheet(specsheet): """ create an OpticalModel with a basic thinlens model, given specsheet """ opt_model = opticalmodel.OpticalModel(specsheet=specsheet) # enter a basic thinlens model for the given specsheet imager = specsheet.imager if specsheet.conjugate_type == 'finite': opt_model.seq_model.gaps[0].thi = -imager.s else: opt_model.seq_model.gaps[0].thi = 1.0e10 opt_model.add_thinlens(power=1/imager.f, indx=1.5, idx=0, t=imager.sp) opt_model.update_model() return opt_model
[docs]def update_specsheet(iid, opt_model): specsheet = opt_model.specsheet specsheet_from_parax_data(opt_model, specsheet) iid.specsheet_dict[specsheet.conjugate_type] = specsheet iid.update_values()
[docs]def create_new_ideal_imager_dialog(**inputs): specsheets = {} conj_type = (inputs['conjugate_type'] if 'conjugate_type' in inputs else 'finite') if 'opt_model' in inputs: opt_model = inputs['opt_model'] specsheet = create_specsheet_from_model(opt_model) conj_type = specsheet.conjugate_type specsheets[conj_type] = specsheet for conj in conjugate_types: if conj != conj_type: specsheets[conj] = create_specsheet(conj) else: specsheets = create_specsheets() if 'gui_parent' in inputs: gui_parent = inputs['gui_parent'] opt_model = gui_parent.app_manager.model iid = IdealImagerDialog(conj_type, specsheets, cmd_fct=gui_parent.handle_ideal_imager_command, parent=gui_parent) gui_parent.add_subwindow(iid, ModelInfo(gui_parent.app_manager.model, update_specsheet, (iid, opt_model))) iid.update_values() iid.show() else: iid = IdealImagerDialog(conj_type, specsheets) iid.update_values() iid.exec_()
[docs]def create_yybar_model(): opt_model = opticalmodel.OpticalModel() # put in minimum calculation defaults opt_model.seq_model.gaps[0].thi = 1.0 opt_model.optical_spec.field_of_view.type = 'OBJ_HT' opt_model.optical_spec.field_of_view.set_from_list([0., 1.]) opt_model.update_model() return opt_model
[docs]def get_defaults_from_gui_parent(gui_parent): if gui_parent: refresh_gui = gui_parent.refresh_gui is_dark = gui_parent.is_dark else: refresh_gui = None is_dark = True return refresh_gui, is_dark
[docs]def create_live_layout_view(opt_model, gui_parent=None): refresh_gui, is_dark = get_defaults_from_gui_parent(gui_parent) fig = InteractiveLayout(opt_model, refresh_gui=refresh_gui, do_draw_frame=True, do_draw_axes=False, do_draw_rays=True, do_paraxial_layout=False, is_dark=is_dark) # cmds = create_live_layout_commands(fig) cmds = None view_width = 880 view_ht = 660 title = "Optical Layout" panel_fcts = [create_2d_figure_toolbar, create_draw_rays_groupbox, ] plotview.create_plot_view(gui_parent, fig, title, view_width, view_ht, add_panel_fcts=panel_fcts, commands=cmds, drop_action=layout.GlassDropAction())
[docs]def create_live_layout_commands(fig): lo = fig.layout cmds = [] # Add thin lens cmds.append(('Add Thin Lens', (lo.register_commands, (), {'apply_fct': layout.add_thinlens}))) # Add lens cmds.append(('Add Lens', (lo.register_commands, (), {'apply_fct': layout.add_lens}))) # Add doublet cmds.append(('Add Cemented Doublet', (lo.register_commands, (), {'apply_fct': layout.add_doublet}))) # Add mirror cmds.append(('Add Mirror', (lo.register_commands, (), {'apply_fct': layout.add_mirror, 'profile': Spherical}))) cmds.append(('Add Conic Mirror', (lo.register_commands, (), {'apply_fct': layout.add_conic, 'profile': Conic}))) cmds.append(('Add Parabola', (lo.register_commands, (), {'apply_fct': layout.add_conic, 'profile': Conic, 'cc': -1.0}))) return cmds
[docs]def create_paraxial_design_view_v2(opt_model, dgm_type, gui_parent=None): refresh_gui, is_dark = get_defaults_from_gui_parent(gui_parent) fig = dgm.InteractiveDiagram(opt_model, dgm_type, refresh_gui=refresh_gui, do_draw_frame=True, do_draw_axes=True, aspect='auto', is_dark=is_dark) panel_fcts = [create_2d_figure_toolbar, ] if dgm_type == 'ht': cmds = create_parax_design_commands(fig) panel_fcts.append(create_diagram_controls_groupbox) panel_fcts.append(create_diagram_edge_actions_groupbox) panel_fcts.append(create_diagram_layers_groupbox) elif dgm_type == 'slp': panel_fcts.append(create_diagram_layers_groupbox) cmds = None else: cmds = None view_width = 880 view_ht = 660 title = "Paraxial Design View" plotview.create_plot_view(gui_parent, fig, title, view_width, view_ht, add_panel_fcts=panel_fcts, commands=cmds, drop_action=diagram.GlassDropAction())
[docs]def create_parax_design_commands(fig): cmds = [] dgm = fig.diagram # initialize dgm with a Select command dgm.register_commands((), figure=fig) # Select an existing point cmds.append(('Select', (dgm.register_commands, (), {}))) # Add thin lens cmds.append(('Add Thin Lens', (dgm.register_add_replace_element, (), {'node_init': ele.create_thinlens, 'factory': ele.create_thinlens, 'interact_mode': 'transmit'}))) # Add lens kwargs = {'node_init': ele.create_thinlens, 'factory': ele.create_lens, 'interact_mode': 'transmit'} cmds.append(('Add Lens', (dgm.register_add_replace_element, (), kwargs))) # Add doublet kwargs = {'node_init': ele.create_thinlens, 'factory': ele.create_cemented_doublet, 'interact_mode': 'transmit'} cmds.append(('Add Cemented Doublet', (dgm.register_add_replace_element, (), kwargs))) # Add mirror cmds.append(('Add Mirror', (dgm.register_add_replace_element, (), {'node_init': ele.create_mirror, 'factory': ele.create_mirror, 'interact_mode': 'reflect'}))) # Replace with file pth = pathlib.Path(__file__).resolve() try: rayoptics_pos = pth.parts.index('rayoptics') except ValueError: logger.debug("Can't find rayoptics: path is %s", pth) else: # models_dir = rayoptics/models models_dir = pathlib.Path(*pth.parts[:rayoptics_pos+1]) / 'models' filepath = models_dir / 'Sasian Triplet.roa' def cff(**kwargs): return ele.create_from_file(filepath, **kwargs) cmds.append(('Sasian Triplet', (dgm.register_add_replace_element, (), {'filename': filepath, 'node_init': ele.create_thinlens, 'factory': cff, 'interact_mode': 'transmit'}))) finally: return cmds
[docs]def set_vignetting(opt_model, gui_parent=None): """ From existing fields and clear apertures, calculate vignetting. """ vigcalc.set_vig(opt_model) if gui_parent is None: opt_model.update_model(src_model=opt_model['seq_model']) else: gui_parent.refresh_gui(src_model=opt_model['seq_model'])
[docs]def set_apertures(opt_model, gui_parent=None): """ From existing fields and vignetting, calculate clear apertures. """ vigcalc.set_ape(opt_model) if gui_parent is None: opt_model.update_model(src_model=opt_model['seq_model']) else: gui_parent.refresh_gui(src_model=opt_model['seq_model'])
[docs]def set_pupil(opt_model, gui_parent=None): """ From existing stop size, calculate pupil spec and vignetting. """ vigcalc.set_pupil(opt_model) if gui_parent is None: opt_model.update_model(src_model=opt_model['seq_model']) else: gui_parent.refresh_gui(src_model=opt_model['seq_model'])
[docs]def refocus(opt_model, gui_parent=None): """ Compute a focus shift bringing the axial marginal ray to zero. """ focus_shift = trace.refocus(opt_model) opt_model['optical_spec']['focus'].focus_shift = focus_shift if gui_parent is None: opt_model.update_model(src_model=opt_model['optical_spec']) else: gui_parent.refresh_gui(src_model=opt_model['optical_spec'])
[docs]def create_ray_fan_view(opt_model, data_type, gui_parent=None): refresh_gui, is_dark = get_defaults_from_gui_parent(gui_parent) fig = RayFanFigure(opt_model, data_type, scale_type=Fit.All_Same, figsize=(5, 4), dpi=100, is_dark=is_dark) view_width = 600 view_ht = 600 if data_type == "Ray": title = "Ray Fan View" elif data_type == "OPD": title = "OPD Fan View" else: title = "bad data_type argument" panel_fcts = [create_multi_plot_scale_panel] plotview.create_plot_view(gui_parent, fig, title, view_width, view_ht, add_panel_fcts=panel_fcts)
[docs]def create_ray_grid_view(opt_model, gui_parent=None): refresh_gui, is_dark = get_defaults_from_gui_parent(gui_parent) num_flds = len(opt_model.optical_spec.field_of_view.fields) fig = SpotDiagramFigure(opt_model, scale_type=Fit.All_Same, dpi=100, is_dark=is_dark) view_box = 300 view_width = view_box view_ht = num_flds * view_box title = "Spot Diagram" panel_fcts = [create_multi_plot_scale_panel] plotview.create_plot_view(gui_parent, fig, title, view_width, view_ht, add_panel_fcts=panel_fcts)
[docs]def create_wavefront_view(opt_model, gui_parent=None): refresh_gui, is_dark = get_defaults_from_gui_parent(gui_parent) num_flds = len(opt_model.optical_spec.field_of_view.fields) num_wvls = len(opt_model.optical_spec.spectral_region.wavelengths) fig = WavefrontFigure(opt_model, scale_type=Fit.All_Same, num_rays=32, dpi=100, is_dark=is_dark) # figsize=(2*num_wvls, 2*num_flds)) view_box = 300 view_width = num_wvls * view_box view_ht = num_flds * view_box title = "Wavefront Map" panel_fcts = [create_multi_plot_scale_panel] plotview.create_plot_view(gui_parent, fig, title, view_width, view_ht, add_panel_fcts=panel_fcts)
[docs]def create_field_curves(opt_model, gui_parent=None): refresh_gui, is_dark = get_defaults_from_gui_parent(gui_parent) fig = FieldCurveFigure(opt_model, dpi=100, is_dark=is_dark) view_width = 600 view_ht = 600 title = "Field Curves" panel_fcts = [create_plot_scale_panel] plotview.create_plot_view(gui_parent, fig, title, view_width, view_ht, add_panel_fcts=panel_fcts)
[docs]def create_3rd_order_bar_chart(opt_model, gui_parent=None): refresh_gui, is_dark = get_defaults_from_gui_parent(gui_parent) fig = ThirdOrderBarChart(opt_model, dpi=100, is_dark=is_dark) view_width = 600 view_ht = 600 title = "3rd Order Aberrations" panel_fcts = [create_plot_scale_panel] plotview.create_plot_view(gui_parent, fig, title, view_width, view_ht, add_panel_fcts=panel_fcts)
[docs]def create_glass_map_view(opt_model, gui_parent=None): refresh_gui, is_dark = get_defaults_from_gui_parent(gui_parent) glass_names = set() glasses = list() for g in opt_model.seq_model.gaps: m = g.medium if not isinstance(m, om.Air): if m.name() not in glass_names: glass_names.add(m.name()) glasses.append(m) glass_db = gm.GlassMapDB(glasses, gfact._catalog_list) plotview.create_glass_map_view(gui_parent, glass_db)
[docs]def update_table_view(table_view): table_model = table_view.model() table_model.endResetModel()
[docs]def create_lens_table_model(seq_model): def replace_glass(event, index): mime = event.mimeData() # comma separated list glass_name, catalog_name = mime.text().split(',') mat = gfact.create_glass(glass_name, catalog_name) seq_model.gaps[index].medium = mat colEvalStr = ['.ifcs[{}].interface_type()', '.ifcs[{}].profile_cv', '.gaps[{}].thi', '.gaps[{}].medium.name()', '.ifcs[{}].interact_mode', '.ifcs[{}].surface_od()'] rowHeaders = seq_model.surface_label_list() colHeaders = ['type', 'cv', 'thi', 'medium', 'mode', 'sd'] colFormats = ['{:s}', '{:12.7g}', '{:12.5g}', '{:s}', '{:s}', '{:12.5g}'] drop_actions = [None]*len(colHeaders) drop_actions[3] = replace_glass return PyTableModel(seq_model, '', colEvalStr, rowHeaders, colHeaders, colFormats, True, get_num_rows=seq_model.get_num_surfaces, get_row_headers=seq_model.surface_label_list, drop_actions=drop_actions)
[docs]def create_element_table_model(opt_model): ele_model = opt_model.ele_model def get_row_headers(): return [str(i) for i in range(ele_model.get_num_elements())] colEvalStr = ['.elements[{}].label', '.element_type({})', '.elements[{}].medium_name', '.elements[{}].tfrm[1][1]', '.elements[{}].tfrm[1][2]', '.elements[{}].reference_idx()'] rowHeaders = get_row_headers() colHeaders = ['label', 'type', 'medium', 'y', 'z', 'idx'] colFormats = ['{:s}', '{:s}', '{:s}', '{:12.5g}', '{:12.5g}', '{:d}'] return PyTableModel(ele_model, '', colEvalStr, rowHeaders, colHeaders, colFormats, True, get_num_rows=ele_model.get_num_elements, get_row_headers=get_row_headers)
[docs]def create_ray_table_model(opt_model, ray): colEvalStr = ['[{}].p[0]', '[{}].p[1]', '[{}].p[2]', '[{}].d[0]', '[{}].d[1]', '[{}].d[2]', '[{}].dst'] seq_model = opt_model.seq_model rowHeaders = seq_model.surface_label_list() colHeaders = ['x', 'y', 'z', 'l', 'm', 'n', 'length'] colFormats = ['{:12.5g}', '{:12.5g}', '{:12.5g}', '{:9.6f}', '{:9.6f}', '{:9.6f}', '{:12.5g}'] return PyTableModel(ray, '', colEvalStr, rowHeaders, colHeaders, colFormats, False, get_num_rows=seq_model.get_num_surfaces, get_row_headers=seq_model.surface_label_list)
[docs]def create_parax_table_model(opt_model): rootEvalStr = ".analysis_results['parax_data']" colEvalStr = ['[0][{}][0]', '[0][{}][1]', '[0][{}][2]', '[1][{}][0]', '[1][{}][1]', '[1][{}][2]'] seq_model = opt_model.seq_model rowHeaders = seq_model.surface_label_list() colHeaders = ['y', 'u', 'i', 'y-bar', 'u-bar', 'i-bar'] colFormats = ['{:12.5g}', '{:9.6f}', '{:9.6f}', '{:12.5g}', '{:9.6f}', '{:9.6f}'] return PyTableModel(opt_model, rootEvalStr, colEvalStr, rowHeaders, colHeaders, colFormats, False, get_num_rows=seq_model.get_num_surfaces, get_row_headers=seq_model.surface_label_list)
[docs]def create_parax_model_table(opt_model): rootEvalStr = ".parax_model" colEvalStr = ['.ax[{}][0]', '.pr[{}][0]', '.ax[{}][1]', '.pr[{}][1]', '.sys[{}][0]', '.sys[{}][1]', '.sys[{}][2]', '.sys[{}][3]'] seq_model = opt_model.seq_model rowHeaders = seq_model.surface_label_list() colHeaders = ['y', 'y-bar', 'nu', 'nu-bar', 'pwr', 'tau', 'n after', 'mode'] colFormats = ['{:12.5g}', '{:12.5g}', '{:9.6f}', '{:9.6f}', '{:12.7g}', '{:12.5g}', '{:7.4f}', '{:s}'] return PyTableModel(opt_model, rootEvalStr, colEvalStr, rowHeaders, colHeaders, colFormats, True, get_num_rows=seq_model.get_num_surfaces, get_row_headers=seq_model.surface_label_list)