diff --git a/doc/source/api_ref/index.rst b/doc/source/api_ref/index.rst index 9079ffb..baaa197 100644 --- a/doc/source/api_ref/index.rst +++ b/doc/source/api_ref/index.rst @@ -9,5 +9,6 @@ API Reference faces shapes materials + distortions core diff --git a/doc/source/distortions.rst b/doc/source/distortions.rst index 3e12b29..8169496 100644 --- a/doc/source/distortions.rst +++ b/doc/source/distortions.rst @@ -56,30 +56,15 @@ More general distortions can be applied using the :py:class:`raypier.distortions As previously, instances of this object are passed to a :py:class:`raypier.faces.DistortionFace` , along with the base-surface to which the distortion is to be applied. - -.. py:class:: ZernikeSeries(unit_radius=10.0, coefficients=[]) - :canonical: raypier.distortions.ZernikeSeries - - Describes a general distortion defined in terms of Zernike polynomials. - The ANSI single-index (J) notation is used to identify coefficients. - - A recursive algorithm is used for evaluation of the function with caching - for efficient evaluation where many non-zero coefficients exist. - - Both the given parameters are traits on this object and updates to either - one will automatically update the internal state of the object (and trigger - re-tracing of the model). - - :param float unit_radius: Specifies the unit-radius for the Zernike polynomials. - :param list coefficients: A sequence of coefficient amplitudes, given a tuples (J, amplitude). - E.g. [(0,0.1),(2,0.3),(7,2.34)]. Any number of coefficients - can be specified. - An example of the this class in action can be seen here: .. literalinclude:: /../../examples/zernike_distortion_example.py -.. image:: images/zernike_distortion_example.png +Here's what the model looks like in the UI. + +.. image:: images/zernike_distortions_example.png +This example also demonstrates the use of a Constraints object to provide some UI controls for +easier adjustment of the relevant model parameters. diff --git a/doc/source/gaussian_beamlet_propagation.rst b/doc/source/gaussian_beamlet_propagation.rst index 8438018..86d2e4f 100644 --- a/doc/source/gaussian_beamlet_propagation.rst +++ b/doc/source/gaussian_beamlet_propagation.rst @@ -46,33 +46,34 @@ Evaluating the E-field The nice thing about Gausslet ray-tracing is that you can evaluate the E-field at any point in your model. For script-based analysis, you can give any GaussletCollection object (obtained from a source-object after a tracing operation) to the - :py:function:`raypier.core.fields.eval_Efield_from_gausslets` function. +:py:func:`raypier.core.fields.eval_Efield_from_gausslets` function. .. py:module:: raypier.core.fields - .. py:function:: eval_Efield_from_gausslets(gc : GaussletCollection, points : ndarray[:,3], wavelengths=None, blending=1.0) -> Efield ndarray[:,3] - :canonical: raypier.core.fields.eval_Efield_from_gausslets - - Calculates the vector E-field is each of the points given. The returned - array of field-vectors will have the same length as `points` and - has `numpy.complex128` dtype. - - :param GaussletCollection gc: The set of Gausslets for which the field should be calculated - :param ndarray[N,3] points: An array of shape (N,3) giving the points at which the field will be evaluated. - :param ndarray[] wavelengths: A 1d array containing the wavelengths to be used for the field calculation, - overriding the wavelengths data contained by the GaussletCollection object. - :param float blending: The 1/width of each Gaussian mode at the evaluation points. A value of unity (the default), - means the parabasal rays are determined to be the 1/e point in the field amplitude. - - .. py:class:: EFieldSummation(gc : GaussletCollection, points : ndarray[:,3], wavelengths=None, blending=1.0) -> EFieldSummation object - - For situations where you wish to evaluate the E-field from a set of Gausslets with different sets of evaluation points, - this class provides a small optimisation by performing the maths to convert ray-intercepts to Gaussian mode parameters - up front. - - .. py:method:: evaluate(points : ndarray[:,3]) -> Efield ndarray[:,3] - - Called to calculate the E-field for the given points. +Algorithms for evaluation of E-fields are provided here. + + .. py:function:: raypier.core.fields.eval_Efield_from_gausslets(gc : GaussletCollection, points : ndarray[N,3], wavelengths=None, blending=1.0) -> Efield ndarray[N,3] + + Calculates the vector E-field is each of the points given. The returned + array of field-vectors will have the same length as `points` and + has `numpy.complex128` dtype. + + :param GaussletCollection gc: The set of Gausslets for which the field should be calculated + :param ndarray[N,3] points: An array of shape (N,3) giving the points at which the field will be evaluated. + :param ndarray[] wavelengths: A 1d array containing the wavelengths to be used for the field calculation, + overriding the wavelengths data contained by the GaussletCollection object. + :param float blending: The 1/width of each Gaussian mode at the evaluation points. A value of unity (the default), + means the parabasal rays are determined to be the 1/e point in the field amplitude. + +.. py:class:: EFieldSummation(gc : GaussletCollection, points : ndarray[N,3], wavelengths=None, blending=1.0) -> EFieldSummation object + + For situations where you wish to evaluate the E-field from a set of Gausslets with different sets of evaluation points, + this class provides a small optimisation by performing the maths to convert ray-intercepts to Gaussian mode parameters + up front. + + .. py:method:: evaluate(points : ndarray[N,3]) -> Efield ndarray[N,3] + + Called to calculate the E-field for the given points. Beam Decomposition @@ -90,56 +91,55 @@ will, in general, not join originate from the end-point of the input-rays. High level `Optics` objects for beam decomposition are provided here. -.. py:class:: PositionDecompositionPlane(BaseDecompositionPlane) - :canonical: raypier.gausslets.PositionDecompositionPlane +.. py:class:: raypier.gausslets.PositionDecompositionPlane(BaseDecompositionPlane) Defines a plane at which position-decomposition will be beformed. - .. py:attribute:: radius - :type: float + .. py:attribute:: radius + Sets the radius used for capturing incoming rays. Rays outside of this will "miss" .. py:attribute:: curvature - :type: float + An approximate radius-of-curvature to the beam focus. This is used to improve the phase-unwrapping of the wavefront. The default is zero, which means a plane-wave is assumed. Negative values imply a focus behind the decomposition plane (i.e. on the opposite side to the plane direction vector). .. py:attribute:: resolution - :type: float + Sets the resampling density of the decomposition, in terms of the number of new rays per `radius` extent. .. py:attribute:: blending - :type: float + Sets the blending values for the new rays. The new rays will have Gaussian 1/e**2 intensity widths equal to `spacing`/`blending`, where the `spacing` value is `radius`/`resolution`. - .. py:class:: AngleDecomposition(BaseDecompositionPlane) +.. py:class:: AngleDecomposition(BaseDecompositionPlane) Defines a plane at which Gabor (angle)-decomposition is to be performed. .. py:attribute:: sample_spacing - :type: float + Sets the sample-spacing at the decomposition plane, in microns. .. py:attribute:: width - :type: int + A value in the range 1->512 to set the number of samples along the width of the sample-plane. .. py:attribute:: height - :type: int + A value in the range 1->512 to set the number of samples along the height of the sample-plane. .. py:attribute:: mask - :type: ndarray[:,:] + A 2d array with shape matching the (width, height) and dtype numpy.float64 . The array values should be in the range 0.0 -> 1.0. This will be used to mask the input E-field. .. py:attribute:: max_angle - :type: float + Limits the angular divergence of the outgoing rays. diff --git a/raypier/core/cshapes.pyx b/raypier/core/cshapes.pyx index d771d1d..ac3b1b6 100644 --- a/raypier/core/cshapes.pyx +++ b/raypier/core/cshapes.pyx @@ -15,6 +15,8 @@ from .ctracer cimport Shape, sep_, \ vector_t, subvv_, dotprod_, mag_sq_, norm_,\ addvv_, multvs_, mag_, cross_ +import numpy as np + cdef class LogicalOpShape(Shape): def __and__(self, Shape other): @@ -133,3 +135,39 @@ cdef class RectangleShape(BasicShape): return 1 else: return 0 + + +cdef class PolygonShape(BasicShape): + cdef: + double[:,:] _coordinates + + property coordinates: + def __get__(self): + return np.asarray(self._coordinates) + + def __set__(self, val): + self._coordinates = val + + cdef bint point_inside_c(self, double X, double Y): + cdef: + int i, size, ct=0 + double y1, y2, h, x, x1, x2 + double[:,:] pts = self._coordinates + + size = pts.shape[0] + + y1 = pts[size-1,1] + x1 = pts[size-1,0] + for i in range(size): + y2 = pts[i,1] + x2 = pts[i,0] + h = (Y - y1) / (y2 - y1) + if 0 < h <= 1.0: + x = x1 + h*(x2 - x1) + if x > X: + ct = not ct + y1 = y2 + x1 = x2 + return ct!=0 + + \ No newline at end of file diff --git a/raypier/distortions.py b/raypier/distortions.py index e67fa8f..96c99d0 100644 --- a/raypier/distortions.py +++ b/raypier/distortions.py @@ -18,13 +18,24 @@ class BaseDistortion(HasTraits): class NullDistortion(BaseDistortion): + """Any zero-amplitude distortion. Mainly used in testing. + """ pass class SimpleTestZernikeJ7(BaseDistortion): + """ + An implementation of a specific Zernike polynomial + distortion, using J=7. Mainly used in testing. + """ + + #: Defines the unit-radius for the Zernike polynomial function. unit_radius = Float(10.0) + + #: RMS amplitude of the distortion amplitude = Float(0.001) + #: The underlying core.Distortion object c_distortion = Instance(cdistortions.SimpleTestZernikeJ7, ()) traits_view = View(VGroup( @@ -48,8 +59,29 @@ def on_params_change(self, evt): class ZernikeSeries(BaseDistortion): + """ + Represents a general surface distortion in terms of a sequence of Zernike polynomials. + The Zernike terms are identified using the ANSI standard single-index notation (J). + + The standard normalisation is used such that the amplitude coefficients give the RMS + deviation of the surface over the unit-disk. + + A recursive algorithm is used for evaluation of the function with caching + for efficient evaluation where many non-zero coefficients exist. + + Both the given parameters are traits on this object and updates to either + one will automatically update the internal state of the object (and trigger + re-tracing of the model). + + This class is intended to be used with the DistortionSurface to wrap any underlying ShapedFace. + """ + + #: The unit-radius for the Zernike polynomial radial function unit_radius = Float(10.0) + #: A list of (int,float) tuples giving the amplitudes of all non-zero Zernike coefficients. + #: in terms of the ANSI standard single index J = (n*(n+1) + m)/2. + #: Any number of coefficients can be given. coefficients = List(Tuple(Int,Float)) c_distortion = Instance(cdistortions.ZernikeDistortion)