def test_multiple_relative_transform_paths_are_converted_to_absolute_path_in_dependee_of_field( file, nexus_wrapper): component_name = "component_1" component1 = add_component_to_file(nexus_wrapper, component_name=component_name) # make depends_on point to relative transformations group component1.group["depends_on"] = "transformations/transform1" transformations_group = component1.group.create_group("transformations") transform1_name = "transform1" transform1_dataset = transformations_group.create_dataset(transform1_name, data=1) transform1_dataset.attrs[CommonAttrs.VECTOR] = qvector3d_to_numpy_array( QVector3D(1, 0, 0)) transform1_dataset.attrs[ CommonAttrs.TRANSFORMATION_TYPE] = TransformationType.TRANSLATION transform2_name = "transform2" # make transform1 depends_on point to relative transform in same directory transform1_dataset.attrs["depends_on"] = transform2_name transform2_dataset = transformations_group.create_dataset(transform2_name, data=2) transform2_dataset.attrs[CommonAttrs.VECTOR] = qvector3d_to_numpy_array( QVector3D(1, 1, 0)) transform2_dataset.attrs[ CommonAttrs.TRANSFORMATION_TYPE] = TransformationType.TRANSLATION # make sure the depends_on points to the absolute path of the transform it depends on in the file assert (Transformation( nexus_wrapper, transform1_dataset).depends_on.dataset.name == transform2_dataset.name)
def add_rotation( self, axis: QVector3D, angle: float, name: str = None, depends_on: Transformation = None, ) -> Transformation: """ Note, currently assumes angle is in degrees :param axis: axis :param angle: :param name: Name of the rotation group (Optional) :param depends_on: existing transformation which the new one depends on (otherwise relative to origin) """ transforms_group = self.file.create_transformations_group_if_does_not_exist( self.group) if name is None: name = _generate_incremental_name(TransformationType.ROTATION, transforms_group) field = self.file.set_field_value(transforms_group, name, angle, float) self.file.set_attribute_value(field, CommonAttrs.UNITS, "degrees") self.file.set_attribute_value(field, CommonAttrs.VECTOR, qvector3d_to_numpy_array(axis)) self.file.set_attribute_value(field, CommonAttrs.TRANSFORMATION_TYPE, TransformationType.ROTATION) rotation_transform = Transformation(self.file, field) rotation_transform.depends_on = depends_on rotation_transform.ui_value = angle return rotation_transform
def _create_transform( self, name: str, transformation_type: TransformationType, angle_or_magnitude: float, units: str, vector: QVector3D, depends_on: Transformation, ): transforms_group = self.file.create_transformations_group_if_does_not_exist( self.group ) if name is None: name = _generate_incremental_name(transformation_type, transforms_group) field = self.file.set_field_value( transforms_group, name, angle_or_magnitude, float ) self.file.set_attribute_value(field, CommonAttrs.UNITS, units) self.file.set_attribute_value( field, CommonAttrs.VECTOR, qvector3d_to_numpy_array(vector) ) self.file.set_attribute_value( field, CommonAttrs.TRANSFORMATION_TYPE, transformation_type ) transform = create_transformation(self.file, field) transform.ui_value = angle_or_magnitude transform.depends_on = depends_on return transform
def add_translation(self, vector: QVector3D, name: str = None, depends_on: Transformation = None) -> Transformation: """ Note, currently assumes translation is in metres :param vector: direction and magnitude of translation as a 3D vector :param name: name of the translation group (Optional) :param depends_on: existing transformation which the new one depends on (otherwise relative to origin) """ transforms_group = self.file.create_transformations_group_if_does_not_exist( self.group) if name is None: name = _generate_incremental_name(TransformationType.TRANSLATION, transforms_group) unit_vector, magnitude = _normalise(vector) field = self.file.set_field_value(transforms_group, name, magnitude, float) self.file.set_attribute_value(field, CommonAttrs.UNITS, "m") self.file.set_attribute_value(field, CommonAttrs.VECTOR, qvector3d_to_numpy_array(unit_vector)) self.file.set_attribute_value(field, CommonAttrs.TRANSFORMATION_TYPE, TransformationType.TRANSLATION) translation_transform = Transformation(self.file, field) translation_transform.ui_value = magnitude translation_transform.depends_on = depends_on return translation_transform
def test_GIVEN_a_PixelShape_WHEN_calling_get_shape_THEN_shape_and_transformations_are_returned( nexus_wrapper, ): detector_group = nexus_wrapper.create_nx_group("detector", "NXdetector", nexus_wrapper.instrument) shape_group = nexus_wrapper.create_nx_group("pixel_shape", "NXoff_geometry", detector_group) # Populate shape group vertices = [ [-0.05, -0.05, 0.0], [0.05, -0.05, 0.0], [0.05, 0.05, 0.0], [-0.05, 0.05, 0.0], ] faces = [0] winding_order = [0, 1, 2, 3] vertices_field = nexus_wrapper.set_field_value(shape_group, "vertices", vertices) nexus_wrapper.set_attribute_value(vertices_field, "units", "m") nexus_wrapper.set_field_value(shape_group, "winding_order", winding_order) nexus_wrapper.set_field_value(shape_group, "faces", faces) # Add pixel offsets to detector group x_offsets = np.array([[-0.05, 0.05], [-0.05, 0.05]]) y_offsets = np.array([[-0.05, -0.05], [0.05, 0.05]]) nexus_wrapper.set_field_value(detector_group, "x_pixel_offset", x_offsets) nexus_wrapper.set_field_value(detector_group, "y_pixel_offset", y_offsets) pixel_shape = PixelShape(nexus_wrapper, detector_group) assert isinstance(pixel_shape, PixelShape) shape, transformations = pixel_shape.get_shape() for vertex_index, vertex in enumerate(shape.vertices): assert np.allclose(qvector3d_to_numpy_array(vertex), vertices[vertex_index]) assert np.allclose(shape.faces, [winding_order]) assert (len(transformations) == x_offsets.size ), "Expected one transformation per pixel offset" assert np.allclose(qvector3d_to_numpy_array(transformations[0]), np.array([-0.05, -0.05, 0.0])) assert np.allclose(qvector3d_to_numpy_array(transformations[3]), np.array([0.05, 0.05, 0.0]))
def record_vertices(self, new_vertices: List[QVector3D]): """ Record vertex data in file :param new_vertices: The new vertices data, list of cartesian coords for each vertex """ vertices = np.array( [qvector3d_to_numpy_array(vertex) for vertex in new_vertices]) self.set_field_value(CommonAttrs.VERTICES, vertices, ValueTypes.FLOAT) self[CommonAttrs.VERTICES].attributes.set_attribute_value( CommonAttrs.UNITS, "m")
def record_vertices_in_file(nexus_wrapper: nx.NexusWrapper, group: h5py.Group, new_vertices: List[QVector3D]): """ Record vertex data in file :param nexus_wrapper: Wrapper for the file the data will be stored in :param group: The shape group node :param new_vertices: The new vertices data, list of cartesian coords for each vertex """ vertices = [qvector3d_to_numpy_array(vertex) for vertex in new_vertices] vertices_node = nexus_wrapper.set_field_value(group, CommonAttrs.VERTICES, vertices) nexus_wrapper.set_attribute_value(vertices_node, CommonAttrs.UNITS, "m")
def calculate_vertices(axis_direction: QVector3D, height: float, radius: float) -> np.ndarray: """ Given cylinder axis, height and radius, calculate the base centre, base edge and top centre vertices :param axis_direction: axis of the cylinder (not required to be unit vector) :param height: height of the cylinder :param radius: radius of the cylinder :return: base centre, base edge and top centre vertices as a numpy array """ axis_direction = axis_direction.normalized() top_centre = axis_direction * height / 2.0 base_centre = axis_direction * height / -2.0 radial_direction = get_an_orthogonal_unit_vector( axis_direction).normalized() base_edge = base_centre + (radius * radial_direction) vertices = np.vstack(( qvector3d_to_numpy_array(base_centre), qvector3d_to_numpy_array(base_edge), qvector3d_to_numpy_array(top_centre), )) return vertices
def _add_transform_to_file( nexus_wrapper: NexusWrapper, name: str, value: Any, vector: QVector3D, transform_type: str, ): transform_dataset = nexus_wrapper.nexus_file.create_dataset(name, data=value) transform_dataset.attrs[CommonAttrs.VECTOR] = qvector3d_to_numpy_array( vector) transform_dataset.attrs[CommonAttrs.TRANSFORMATION_TYPE] = transform_type return transform_dataset
def test_transforms_with_no_dependees_return_None_for_depends_on( file, nexus_wrapper): component_name = "component_1" component1 = add_component_to_file(nexus_wrapper, component_name=component_name) # make depends_on point to relative transformations group component1.group["depends_on"] = "transformations/transform1" transformations_group = component1.group.create_group("transformations") transform1_name = "transform1" transform1_dataset = transformations_group.create_dataset(transform1_name, data=1) transform1_dataset.attrs[CommonAttrs.VECTOR] = qvector3d_to_numpy_array( QVector3D(1, 0, 0)) transform1_dataset.attrs[ CommonAttrs.TRANSFORMATION_TYPE] = TransformationType.TRANSLATION transform1_dataset.attrs["depends_on"] = "." transformation = Transformation(nexus_wrapper, transform1_dataset) assert not transformation.depends_on