def __init__(
        self,
        slits: int,
        slit_edges: np.ndarray,
        radius: float,
        slit_height: float,
        angle_units: str,
        slit_height_units: str,
        radius_units: str,
    ):
        """
        Class for storing the chopper input given by the user.
        :param slits: The number of slits in the disk chopper.
        :param slit_edges: The list of slit edge angles in the disk chopper.
        :param radius: The radius of the slit chopper.
        :param slit_height: The slit height.
        :param angle_units: The units of the slit edges.
        :param slit_height_units: The units for the slit length.
        :param radius_units: The units for the radius.
        """
        self._slits = slits
        self._radius = radius
        self._slit_height = slit_height

        # Convert the angles to radians and make sure they are all less then two pi
        slit_edges_factor = calculate_unit_conversion_factor(
            angle_units, RADIANS)
        self._slit_edges = [(edge * slit_edges_factor) % TWO_PI
                            for edge in slit_edges]

        self._slit_height *= calculate_unit_conversion_factor(
            slit_height_units, METRES)
        self._radius *= calculate_unit_conversion_factor(radius_units, METRES)
Пример #2
0
 def _evaluate_ui_scale_factor(self, units):
     try:
         if self.transform_type == TransformationType.TRANSLATION:
             self._ui_scale_factor = calculate_unit_conversion_factor(units, METRES)
         elif self.transform_type == TransformationType.ROTATION:
             self._ui_scale_factor = calculate_unit_conversion_factor(units, DEGREES)
     except Exception:
         pass
Пример #3
0
 def _create_transformation_vectors_for_pixel_offsets(
     self, ) -> Optional[List[QVector3D]]:
     """
     Construct a transformation (as a QVector3D) for each pixel offset
     """
     try:
         units = self.get_field_attribute(X_PIXEL_OFFSET, CommonAttrs.UNITS)
         unit_conversion_factor = calculate_unit_conversion_factor(
             units, METRES)
         x_offsets = self.get_field_value(
             X_PIXEL_OFFSET) * unit_conversion_factor
         y_offsets = self.get_field_value(
             Y_PIXEL_OFFSET) * unit_conversion_factor
     except AttributeError:
         logging.info(
             "In pixel_shape_component expected to find x_pixel_offset and y_pixel_offset datasets"
         )
         return None
     try:
         z_offsets = self.get_field_value(Z_PIXEL_OFFSET)
     except AttributeError:
         z_offsets = np.zeros_like(x_offsets)
     if not isinstance(x_offsets, list):
         x_offsets = x_offsets.flatten()
     if not isinstance(y_offsets, list):
         y_offsets = y_offsets.flatten()
     if not isinstance(z_offsets, list):
         z_offsets = z_offsets.flatten()
     # offsets datasets can be 2D to match dimensionality of detector, so flatten to 1D
     return [
         QVector3D(x, y, z)
         for x, y, z in zip(x_offsets, y_offsets, z_offsets)
     ]
def load_geometry_from_file_object(
        file: StringIO,
        extension: str,
        units: str,
        geometry: OFFGeometry = OFFGeometryNoNexus(),
) -> OFFGeometry:
    """
    Loads geometry from a file object into an OFFGeometry instance

    Supported file types are OFF and STL.

    :param file: The file object to load the geometry from.
    :param units: A unit of length in the form of a string. Used to determine the multiplication factor.
    :param geometry: The optional OFFGeometry to load the geometry data into. If not provided, a new instance will be
    returned.
    :return: An OFFGeometry instance containing that file's geometry, or an empty instance if filename's extension is
    unsupported.
    """

    mult_factor = calculate_unit_conversion_factor(units, METRES)

    if extension == ".off":
        _load_off_geometry(file, mult_factor, geometry)
    elif extension == ".stl":
        _load_stl_geometry(file, mult_factor, geometry)
    else:
        geometry.faces = []
        geometry.vertices = []
        logging.error("geometry file extension not supported")

    return geometry
def test_unit_conversion_factor():

    # List of units and their expected value in metres
    units = [("cm", 0.01), ("km", 1000), ("m", 1.0), ("inch", 0.0254), ("foot", 0.3048)]

    # Check that the unit conversion factor can correctly find the unit in terms of meters
    for unit in units:
        assert approx(calculate_unit_conversion_factor(unit[0], METRES)) == unit[1]
Пример #6
0
def test_if_VALID_unit_is_entered_in_ROTATION_then_ui_scale_factor_is_evaluated_correctly():
    test_type = "rotation"

    transformation = create_transform(type=test_type, units="deg")
    assert transformation._ui_scale_factor == 1

    transformation.units = "radian"
    assert transformation._ui_scale_factor == calculate_unit_conversion_factor(
        RADIANS, DEGREES
    )
Пример #7
0
    def off_geometry(self) -> OFFGeometry:
        steps: int = 10
        unit_conversion_factor = calculate_unit_conversion_factor(
            self.units, METRES)

        # A list of vertices describing the circle at the bottom of the cylinder
        bottom_circle = [
            QVector3D(sin(2 * pi * i / steps), cos(2 * pi * i / steps), 0) *
            self.radius for i in range(steps)
        ]

        # The top of the cylinder is the bottom shifted upwards
        top_circle = [
            vector + QVector3D(0, 0, self.height) for vector in bottom_circle
        ]

        # The true cylinder are all vertices from the unit cylinder multiplied by the conversion factor
        vertices = [
            vector * unit_conversion_factor
            for vector in bottom_circle + top_circle
        ]

        # rotate each vertex to produce the desired cylinder mesh
        rotate_matrix = self._rotation_matrix()
        vertices = [vector * rotate_matrix for vector in vertices]

        def vertex_above(vertex):
            """
            Returns the index of the vertex above this one in the cylinder.
            """
            return vertex + steps

        def next_vertex(vertex):
            """
            Returns the next vertex around in the top or bottom circle of the cylinder.
            """
            return (vertex + 1) % steps

        # Rectangular faces joining the top and bottom
        rectangle_faces = [[
            i,
            vertex_above(i),
            vertex_above(next_vertex(i)),
            next_vertex(i)
        ] for i in range(steps)]

        # Step sided shapes describing the top and bottom
        # The bottom uses steps of -1 to preserve winding order
        top_bottom_faces = [
            [i for i in range(steps)],
            [i for i in range((2 * steps) - 1, steps - 1, -1)],
        ]

        return OFFGeometryNoNexus(vertices=vertices,
                                  faces=rectangle_faces + top_bottom_faces)