Source code for rayoptics.elem.transform

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright © 2018 Michael J. Hayford
""" Useful transforms for processing sequential models

.. Created on Fri Feb  9 10:09:58 2018

.. codeauthor: Michael J. Hayford
"""

import numpy as np
import itertools


[docs] def compute_global_coords(seq_model, glo=1, origin=None): """ Return global surface coordinates (rot, t) wrt surface `glo`. Args: seq_model: sequential model glo: global reference surface index origin: None | (r_origin, t_origin) The tuple (r_origin, t_origin) is the transform from the desired global origin to the global surface `glo`. """ def accumulate_transforms(seq, b4_seg, transform_calc, tfrm_prev, tfrm_dir: int): b4_ifc, b4_gap, b4_z_dir = b4_seg r_prev, t_prev = tfrm_prev for (ifc, gap, z_dir) in seq: zdist = tfrm_dir * b4_gap.thi r, t = transform_calc(b4_ifc, zdist, ifc) t_new = np.matmul(r_prev, t) + t_prev r_new = np.matmul(r_prev, r) tfrms.append((r_new, t_new)) r_prev, t_prev = r_new, t_new b4_ifc, b4_gap, b4_z_dir = ifc, gap, z_dir # Initialize origin of global coordinate system. tfrms = [] if origin is None: r_origin, t_origin = np.identity(3), np.array([0., 0., 0.]) else: r_origin, t_origin = origin tfrm_origin = r_origin, t_origin tfrms.append(tfrm_origin) # Compute transforms from global surface to object surface if glo > 0: # iterate in reverse over the segments before the # global reference surface step = -1 seq = itertools.zip_longest(seq_model.ifcs[glo::step], seq_model.gaps[glo-1::step], seq_model.z_dir[glo-1::step]) b4_seg = next(seq) # loop of remaining surfaces in path accumulate_transforms(seq, b4_seg, reverse_transform, tfrm_origin, -1) tfrms.reverse() # Compute transforms from global surface to image surface seq = itertools.zip_longest(seq_model.ifcs[glo:], seq_model.gaps[glo:], seq_model.z_dir[glo:]) b4_seg = next(seq) accumulate_transforms(seq, b4_seg, forward_transform, tfrm_origin, +1) return tfrms
[docs] def compute_local_transforms(seq_model, seq, step): """ Return forward surface coordinates (r.T, t) for each interface. """ def local_transform(seq, transform_calc, tfrm_dir: int): b4_ifc, b4_gap = next(seq) for (ifc, gap) in seq: zdist = tfrm_dir * b4_gap.thi r, t = transform_calc(b4_ifc, zdist, ifc) rt = r.transpose() tfrms.append((rt, t)) b4_ifc, b4_gap = ifc, gap return tfrms num_ifcs = seq_model.get_num_surfaces() tfrms = [] if step == -1: if seq is None: seq = itertools.zip_longest(seq_model.ifcs[num_ifcs::step], seq_model.gaps[num_ifcs-1::step]) tfrms = local_transform(seq, reverse_transform, -1) elif step == 1: if seq is None: seq = itertools.zip_longest(seq_model.ifcs[::step], seq_model.gaps[::step]) tfrms = local_transform(seq, forward_transform, 1) else: tfrms = [] tfrms.append((np.identity(3), np.array([0., 0., 0.]))) return tfrms
[docs] def list_tfrms(tfrms, sel: str='r+t', *args): """ Formatted output of transform lists. Args: tfrms: list of transforms to be output sel: output selector, rotations (r), translations (t) or both (r+t) Returns: tfrms """ has_labels = True if len(tfrms[0]) == 3 else False for i, tfrm in enumerate(tfrms): if has_labels: r, t, label = tfrm lbl = f"{label+':':7s}" spcr = ' ' else: r, t = tfrm lbl = f"{i:2d}:" spcr = ' ' if sel=='r': print(f"{lbl} {r[0][0]:10.6f} {r[0][1]:10.6f} {r[0][2]:10.6f}") print(f"{spcr} {r[1][0]:10.6f} {r[1][1]:10.6f} {r[1][2]:10.6f}") print(f"{spcr} {r[2][0]:10.6f} {r[2][1]:10.6f} {r[2][2]:10.6f}\n") elif sel=='t': print(f"{lbl} {t[0]:12.5f} {t[1]:12.5f} {t[2]:12.5f}") else: print(f"{lbl} {r[0][0]:10.6f} {r[0][1]:10.6f} {r[0][2]:10.6f} {t[0]:12.5f}") print(f"{spcr} {r[1][0]:10.6f} {r[1][1]:10.6f} {r[1][2]:10.6f} {t[1]:12.5f}") print(f"{spcr} {r[2][0]:10.6f} {r[2][1]:10.6f} {r[2][2]:10.6f} {t[2]:12.5f}\n") return tfrms
[docs] def forward_transform(s1, zdist, s2): """ generate transform rotation and translation from s1 coords to s2 coords """ t_orig = np.array([0., 0., zdist]) r_after_s1 = r_before_s2 = None if s1.decenter: r_after_s1, t_after_s1 = s1.decenter.tform_after_surf() t_orig += t_after_s1 if s2.decenter: r_before_s2, t_before_s2 = s2.decenter.tform_before_surf() t_orig += t_before_s2 r_cascade = np.identity(3) if r_after_s1 is not None: t_orig = np.matmul(r_after_s1, t_orig) r_cascade = r_after_s1 if r_before_s2 is not None: r_cascade = np.matmul(r_after_s1, r_before_s2) elif r_before_s2 is not None: r_cascade = r_before_s2 return r_cascade, t_orig
[docs] def reverse_transform(s2, zdist, s1): """ generate transform rotation and translation from s2 coords to s1 coords, applying transforms in the reverse order """ t_orig = np.array([0., 0., zdist]) r_before_s2 = r_after_s1 = None if s2.decenter: r_before_s2, t_before_s2 = s2.decenter.tform_before_surf() t_orig += t_before_s2 if s1.decenter: r_after_s1, t_after_s1 = s1.decenter.tform_after_surf() t_orig += t_after_s1 r_cascade = np.identity(3) if r_before_s2 is not None: r_cascade = r_before_s2.transpose() t_orig = np.matmul(r_cascade, t_orig) if r_after_s1 is not None: r_cascade = np.matmul(r_cascade, r_after_s1.transpose()) elif r_after_s1 is not None: r_cascade = r_after_s1.transpose() return r_cascade, t_orig
[docs] def cascade_transform(r_prev, t_prev, r_seg, t_seg): """ take the seg transform and cascade it with the prev transform """ return r_prev.dot(r_seg), r_prev.dot(t_seg) + t_prev
[docs] def transfer_coords(r_seg, t_seg, pt_s1, dir_s1): """ take p and d in s1 coords of seg and transfer them to s2 coords """ rt = r_seg.transpose() return rt.dot(pt_s1 - t_seg), rt.dot(dir_s1)
[docs] def transform_before_surface(interface, ray_seg): """Transform ray_seg from interface to previous seg. Args: interface: the :class:`~.interface.Interface` for the path sequence ray_seg: ray segment exiting from **interface** Returns: (**b4_pt**, **b4_dir**) - **b4_pt** - ray intersection pt wrt following seg - **b4_dir** - ray direction cosine wrt following seg """ if interface.decenter: # get transformation info after surf r, t = interface.decenter.tform_before_surf() if r is None: b4_pt, b4_dir = (ray_seg[0] - t), ray_seg[1] else: rt = r.transpose() b4_pt, b4_dir = rt.dot(ray_seg[0] - t), rt.dot(ray_seg[1]) else: b4_pt, b4_dir = ray_seg[0], ray_seg[1] return b4_pt, b4_dir
[docs] def transform_after_surface(interface, ray_seg): """Transform ray_seg from interface to following seg. Args: interface: the :class:`~.interface.Interface` for the path sequence ray_seg: ray segment exiting from **interface** Returns: (**b4_pt**, **b4_dir**) - **b4_pt** - ray intersection pt wrt following seg - **b4_dir** - ray direction cosine wrt following seg """ if interface.decenter: # get transformation info after surf r, t = interface.decenter.tform_after_surf() if r is None: b4_pt, b4_dir = (ray_seg[0] - t), ray_seg[1] else: rt = r.transpose() b4_pt, b4_dir = rt.dot(ray_seg[0] - t), rt.dot(ray_seg[1]) else: b4_pt, b4_dir = ray_seg[0], ray_seg[1] return b4_pt, b4_dir