Module ctsimu.scenario.deviation
Geometrical deviation of a coordinate system, i.e. a translation or a rotation with respect to an arbitrary axis.
Expand source code
# -*- coding: UTF-8 -*-
"""
Geometrical deviation of a coordinate system, i.e. a translation or a rotation
with respect to an arbitrary axis.
"""
from ..helpers import *
from ..primitives import Vector, Matrix
from ..geometry import CoordinateSystem, ctsimu_world
from .parameter import Parameter
from .scenevector import Scenevector
class Deviation:
""" Geometrical deviation of a coordinate system,
i.e. a translation or a rotation with respect to one of the
axes x, y, z (world), u, v, w (local), or r, s, t (sample)
or any other arbitrary vector.
Like any parameter, they can have drifts, which means they
can change over time.
Attributes
----------
type : str
Transformation type. Can be `"translation"` or `"rotation"`.
amount : ctsimu.scenario.parameter.Parameter
Transformation amount: length for a translation, angle for a rotation.
axis : ctsimu.scenario.scenevector.Scenevector
Transformation axis: direction for a translation, rotation axis for
a rotation.
pivot : ctsimu.scenario.scenevector.Scenevector
For rotational deviations: position of the rotation's pivot point.
known_to_reconstruction : bool
Should this geometrical deviation be considered by the reconstruction software?
This attribute is obeyed when calculating projection matrices.
"""
def __init__(self, pivot_reference:str="sample", _root=None):
self._root = _root # root scenario object
# The axis and pivot point are `Scenevector`
# objects that can handle vector drifts and
# conversion between coordinate systems:
self.axis = Scenevector(native_unit=None, _root=self._root)
self.pivot = Scenevector(native_unit="mm", _root=self._root)
# Set a standard pivot which refers to the object's center:
self.pivot.set_simple(0, 0, 0)
# Set "sample" as the "most local" pivot reference.
# Will be converted to "local" if necessary:
self.pivot.set_reference(pivot_reference)
# The transformation amount is a
# `Parameter` that can handle drifts.
self.amount = Parameter(native_unit=None, standard_value=0, _root=self._root)
self.set_type("translation")
self.known_to_reconstruction = False
def has_drifts(self) -> bool:
"""Does the deviation specify any drift?
Returns
-------
has_drifts : bool
`True` if the deviation specifies any drift, otherwise `False`.
"""
return self.amount.has_drifts() or self.axis.has_drifts() or self.pivot.has_drifts()
def set_type(self, transformation_type):
"""Set the transformation type.
Parameters
----------
transformation_type : str
Transformation type. `"translation"` or `"rotation"`.
"""
if (transformation_type == "rotation") or (transformation_type == "translation"):
self.type = transformation_type
# Set the correct native unit for the amount:
if self.type == "rotation":
self.amount.set_native_unit("rad")
elif self.type == "translation":
self.amount.set_native_unit("mm")
else:
raise Exception(f"CTSimU Deviation: set_type: Not a valid transformation type: '{transformation_type}'. Valid types are 'translation' and 'rotation'.")
def set_axis(self, axis):
"""Set the transformation axis.
Can be a string identifier: `"x"`, `"y"`, `"z"` (for world axes),
`"u"`, `"v"`, `"w"` (for local axes), `"r"`, `"s"`, `"t"` (for sample axes),
or an arbitrary `ctsimu.scenario.scenevector.Scenevector` object.
Parameters
----------
axis : str or ctsimu.scenario.scenevector.Scenevector
Transformation axis.
"""
if isinstance(axis, str):
if axis in ctsimu_world_axis_designations:
# Given axis is "x", "y" or "z"
# -> vector in world coordinate system
self.axis.set_reference("world")
if axis == "x": self.axis.set_simple(1, 0, 0)
if axis == "y": self.axis.set_simple(0, 1, 0)
if axis == "z": self.axis.set_simple(0, 0, 1)
elif axis in ctsimu_local_axis_designations:
# Given axis is "u", "v" or "w"
# -> vector in local coordinate system
self.axis.set_reference("local")
if axis == "u": self.axis.set_simple(1, 0, 0)
if axis == "v": self.axis.set_simple(0, 1, 0)
if axis == "w": self.axis.set_simple(0, 0, 1)
elif axis in ctsimu_sample_axis_designations:
# Given axis is "r", "s" or "t"
# -> vector in sample coordinate system
self.axis.set_reference("sample")
if axis == "r": self.axis.set_simple(1, 0, 0)
if axis == "s": self.axis.set_simple(0, 1, 0)
if axis == "t": self.axis.set_simple(0, 0, 1)
else:
raise Exception(f"CTSimU Deviation: set_axis: Not a valid axis designation string: '{axis}'. Valid options are: {ctsimu_axis_strings}.")
elif isinstance(axis, Scenevector):
self.axis = axis
else:
raise Exception(f"CTSimU Deviation: set_axis: Not a valid deviation axis type: '{type(axis)}'. Valid options are: str, Scenevector.")
def set_pivot(self, pivot:'Scenevector'):
"""Set the pivot point for rotations.
Parameters
----------
pivot : Scenevector
"""
if isinstance(pivot, Scenevector):
self.pivot = pivot
else:
raise Exception(f"CTSimU Deviation: set_pivot: expects an instance of 'Scenevector', but given pivot is of type {type(pivot)}.")
def set_known_to_reconstruction(self, known_to_reconstruction:bool):
"""Sets the `known_to_reconstruction` attribute.
Parameters
----------
known_to_reconstruction : bool
Should this geometrical deviation be considered by the reconstruction software?
This attribute is obeyed when calculating projection matrices.
"""
if known_to_reconstruction == True:
self.known_to_reconstruction = True
else:
self.known_to_reconstruction = False
def set_amount_from_json(self, json_parameter_object:dict) -> bool:
"""Set the deviation's amount from a JSON object, which
is a parameter with a value and potentially a drift.
This function is usually not called from the outside,
but used by `set_from_json`.
Parameters
----------
json_parameter_object : dict
A CTSimU parameter structure, as imported from a JSON scenario description.
Returns
-------
success : bool
`True` on success, `False` if an error occurred.
"""
return self.amount.set_from_json(json_parameter_object)
def set_from_json(self, json_deviation_object:dict) -> bool:
"""Set the deviation from a CTSimU JSON deviation structure.
Parameters
----------
json_deviation_object : dict
A CTSimU deviation structure, as imported from a JSON scenario description.
Returns
-------
success : bool
`True` on success, `False` if an error occurred.
Raises
------
Exception
When an error occurred.
"""
if json_exists_and_not_null(json_deviation_object, ["type"]):
self.set_type(get_value(json_deviation_object, ["type"]))
else:
raise Exception('CTSimU Deviation: set_from_json: a geometrical deviation must provide a "type": either "rotation" or "translation".')
return False
# Transformation axis:
if json_exists_and_not_null(json_deviation_object, ["axis"]):
axis_obj = json_extract(json_deviation_object, ["axis"])
if isinstance(axis_obj, str):
# Axis is given as a single string.
if axis_obj in ctsimu_axis_strings:
self.set_axis(axis_obj)
else:
raise Exception(f'CTSimU Deviation: set_from_json: The deviation "axis" string is incorrect: must be any of {ctsimu_axis_strings} or a free vector definition.')
return False
elif isinstance(axis_obj, dict):
# Free vector definition.
if self.axis.set_from_json(axis_obj):
# Success.
pass
else:
raise Exception(f'CTSimU Deviation: set_from_json: Failed to set up deviation axis from JSON file. Vector definition seems to be incorrect.')
return False
else:
raise Exception("CTSimU Deviation: set_from_json: Failed to set up deviation axis from JSON structure.")
# Pivot point for rotations:
# Set a standard pivot which refers to the object's center:
self.pivot.set_simple(0, 0, 0)
# Set "sample" as the "most local" pivot reference.
# Will be converted to "local" if necessary:
self.pivot.set_reference("sample")
if json_exists_and_not_null(json_deviation_object, ["pivot"]):
# If another pivot is defined in the
# JSON file, take this one instead...
if self.pivot.set_from_json(json_extract(json_deviation_object, ["pivot"])):
# Success
pass
else:
raise Exception("CTSimU Deviation: set_from_json: Failed to set up deviation's pivot point from JSON structure. Vector definition seems to be incorrect.")
return False
self.set_amount_from_json(json_extract(json_deviation_object, ["amount"]))
self.set_known_to_reconstruction(get_value_in_native_unit(
native_unit="bool",
dictionary=json_deviation_object,
keys=["known_to_reconstruction"],
fail_value=True
))
return True
def json_dict(self) -> dict:
"""Create a dictionary of this deviation for a CTSimU JSON file."""
jd = dict()
jd["type"] = self.type
jd["axis"] = self.axis.json_dict()
if self.type == "rotation":
jd["pivot"] = self.pivot.json_dict()
jd["amount"] = self.amount.json_dict()
jd["known_to_reconstruction"] = self.known_to_reconstruction
return jd
def deviate(self, coordinate_system:'CoordinateSystem', frame:float, n_frames:int, reconstruction=False, attached_to_stage:bool=False, stage_coordinate_system:'CoordinateSystem'=None) -> 'CoordinateSystem':
"""
Apply this deviation to the given coordinate system.
This function checks if the deviation's `known_to_reconstruction`
property applies to this case,
Parameters
----------
coordinate_system : ctsimu.geometry.CoordinateSystem
The coordinate system that will be deviated.
frame : float
Frame number.
n_frames : int
Total number of frames in scan.
reconstruction : bool
If `True`, the deviation is only applied if it is
known to the reconstruction software. The same applies
to whether its drifts are considered or not.
If `False`, the full deviation and all of its drifts will
be applied to the coordinate system.
attached_to_stage : bool
`True` if the coordinate system is attached to the sample stage,
`False` if its reference coordinate system is a world coordinate system.
stage_coordinate_system : ctsimu.geometry.CoordinateSystem, optional
The stage coordinate system. Only necessary if the part is attached
to the sample stage. Otherwise, `None` can be passed.
Returns
-------
deviated_cs : ctsimu.geometry.CoordinateSystem
Coordinate system with the applied deviation. Note that the
original `coordinate_system` passed as an argument will be
altered equally.
"""
if (self.known_to_reconstruction is True) or (reconstruction is False):
amount = self.amount.set_frame_and_get_value(
frame=frame,
n_frames=n_frames,
reconstruction=reconstruction
)
if self.type == "translation":
if self.amount.native_unit == "mm":
if attached_to_stage is False:
# Object in world coordinate system.
# --------------------------------------
# The deviation axis can undergo drifts and could
# be expressed in any coordinate system (world, local, sample).
# Therefore, the axis is a `Scenevector`, which can
# give us the translation vector for the current frame:
translation_axis = self.axis.direction_in_world(
local=coordinate_system,
sample=ctsimu_world,
frame=frame,
n_frames=n_frames,
reconstruction=reconstruction
)
coordinate_system.translate_in_direction(direction=translation_axis, distance=amount)
else:
# Object is in stage coordinate system.
# --------------------------------------
translation_axis = self.axis.direction_in_local(
local=stage_coordinate_system,
sample=coordinate_system,
frame=frame,
n_frames=n_frames,
reconstruction=reconstruction
)
coordinate_system.translate_in_direction(direction=translation_axis, distance=amount)
else:
raise Exception(f"CTSimU Deviation: deviate: Translational deviation must be given in a unit of length. The given unit '{self.amount.native_unit}' cannot be used for a translation.")
elif self.type == "rotation":
if self.amount.native_unit == "rad":
if attached_to_stage is False:
# Object in world coordinate system.
# --------------------------------------
rotation_axis = self.axis.direction_in_world(
local=coordinate_system,
sample=ctsimu_world,
frame=frame,
n_frames=n_frames,
reconstruction=reconstruction
)
pivot_point = self.pivot.point_in_world(
local=coordinate_system,
sample=ctsimu_world,
frame=frame,
n_frames=n_frames,
reconstruction=reconstruction
)
coordinate_system.rotate_around_pivot_point(
axis=rotation_axis, angle=amount, pivot=pivot_point
)
else:
# Object is in stage coordinate system.
# --------------------------------------
rotation_axis = self.axis.direction_in_local(
local=stage_coordinate_system,
sample=coordinate_system,
frame=frame,
n_frames=n_frames,
reconstruction=reconstruction
)
pivot_point = self.pivot.point_in_local(
local=stage_coordinate_system,
sample=coordinate_system,
frame=frame,
n_frames=n_frames,
reconstruction=reconstruction
)
coordinate_system.rotate_around_pivot_point(
axis=rotation_axis, angle=amount, pivot=pivot_point
)
else:
raise Exception(f"CTSimU Deviation: deviate: Rotational deviation must be given in an angular unit. The given unit '{self.amount.native_unit}' cannot be used for a rotation.")
return coordinate_system
Classes
class Deviation (pivot_reference: str = 'sample')
-
Geometrical deviation of a coordinate system, i.e. a translation or a rotation with respect to one of the axes x, y, z (world), u, v, w (local), or r, s, t (sample) or any other arbitrary vector.
Like any parameter, they can have drifts, which means they can change over time.
Attributes
type
:str
- Transformation type. Can be
"translation"
or"rotation"
. amount
:Parameter
- Transformation amount: length for a translation, angle for a rotation.
axis
:Scenevector
- Transformation axis: direction for a translation, rotation axis for a rotation.
pivot
:Scenevector
- For rotational deviations: position of the rotation's pivot point.
known_to_reconstruction
:bool
- Should this geometrical deviation be considered by the reconstruction software? This attribute is obeyed when calculating projection matrices.
Expand source code
class Deviation: """ Geometrical deviation of a coordinate system, i.e. a translation or a rotation with respect to one of the axes x, y, z (world), u, v, w (local), or r, s, t (sample) or any other arbitrary vector. Like any parameter, they can have drifts, which means they can change over time. Attributes ---------- type : str Transformation type. Can be `"translation"` or `"rotation"`. amount : ctsimu.scenario.parameter.Parameter Transformation amount: length for a translation, angle for a rotation. axis : ctsimu.scenario.scenevector.Scenevector Transformation axis: direction for a translation, rotation axis for a rotation. pivot : ctsimu.scenario.scenevector.Scenevector For rotational deviations: position of the rotation's pivot point. known_to_reconstruction : bool Should this geometrical deviation be considered by the reconstruction software? This attribute is obeyed when calculating projection matrices. """ def __init__(self, pivot_reference:str="sample", _root=None): self._root = _root # root scenario object # The axis and pivot point are `Scenevector` # objects that can handle vector drifts and # conversion between coordinate systems: self.axis = Scenevector(native_unit=None, _root=self._root) self.pivot = Scenevector(native_unit="mm", _root=self._root) # Set a standard pivot which refers to the object's center: self.pivot.set_simple(0, 0, 0) # Set "sample" as the "most local" pivot reference. # Will be converted to "local" if necessary: self.pivot.set_reference(pivot_reference) # The transformation amount is a # `Parameter` that can handle drifts. self.amount = Parameter(native_unit=None, standard_value=0, _root=self._root) self.set_type("translation") self.known_to_reconstruction = False def has_drifts(self) -> bool: """Does the deviation specify any drift? Returns ------- has_drifts : bool `True` if the deviation specifies any drift, otherwise `False`. """ return self.amount.has_drifts() or self.axis.has_drifts() or self.pivot.has_drifts() def set_type(self, transformation_type): """Set the transformation type. Parameters ---------- transformation_type : str Transformation type. `"translation"` or `"rotation"`. """ if (transformation_type == "rotation") or (transformation_type == "translation"): self.type = transformation_type # Set the correct native unit for the amount: if self.type == "rotation": self.amount.set_native_unit("rad") elif self.type == "translation": self.amount.set_native_unit("mm") else: raise Exception(f"CTSimU Deviation: set_type: Not a valid transformation type: '{transformation_type}'. Valid types are 'translation' and 'rotation'.") def set_axis(self, axis): """Set the transformation axis. Can be a string identifier: `"x"`, `"y"`, `"z"` (for world axes), `"u"`, `"v"`, `"w"` (for local axes), `"r"`, `"s"`, `"t"` (for sample axes), or an arbitrary `ctsimu.scenario.scenevector.Scenevector` object. Parameters ---------- axis : str or ctsimu.scenario.scenevector.Scenevector Transformation axis. """ if isinstance(axis, str): if axis in ctsimu_world_axis_designations: # Given axis is "x", "y" or "z" # -> vector in world coordinate system self.axis.set_reference("world") if axis == "x": self.axis.set_simple(1, 0, 0) if axis == "y": self.axis.set_simple(0, 1, 0) if axis == "z": self.axis.set_simple(0, 0, 1) elif axis in ctsimu_local_axis_designations: # Given axis is "u", "v" or "w" # -> vector in local coordinate system self.axis.set_reference("local") if axis == "u": self.axis.set_simple(1, 0, 0) if axis == "v": self.axis.set_simple(0, 1, 0) if axis == "w": self.axis.set_simple(0, 0, 1) elif axis in ctsimu_sample_axis_designations: # Given axis is "r", "s" or "t" # -> vector in sample coordinate system self.axis.set_reference("sample") if axis == "r": self.axis.set_simple(1, 0, 0) if axis == "s": self.axis.set_simple(0, 1, 0) if axis == "t": self.axis.set_simple(0, 0, 1) else: raise Exception(f"CTSimU Deviation: set_axis: Not a valid axis designation string: '{axis}'. Valid options are: {ctsimu_axis_strings}.") elif isinstance(axis, Scenevector): self.axis = axis else: raise Exception(f"CTSimU Deviation: set_axis: Not a valid deviation axis type: '{type(axis)}'. Valid options are: str, Scenevector.") def set_pivot(self, pivot:'Scenevector'): """Set the pivot point for rotations. Parameters ---------- pivot : Scenevector """ if isinstance(pivot, Scenevector): self.pivot = pivot else: raise Exception(f"CTSimU Deviation: set_pivot: expects an instance of 'Scenevector', but given pivot is of type {type(pivot)}.") def set_known_to_reconstruction(self, known_to_reconstruction:bool): """Sets the `known_to_reconstruction` attribute. Parameters ---------- known_to_reconstruction : bool Should this geometrical deviation be considered by the reconstruction software? This attribute is obeyed when calculating projection matrices. """ if known_to_reconstruction == True: self.known_to_reconstruction = True else: self.known_to_reconstruction = False def set_amount_from_json(self, json_parameter_object:dict) -> bool: """Set the deviation's amount from a JSON object, which is a parameter with a value and potentially a drift. This function is usually not called from the outside, but used by `set_from_json`. Parameters ---------- json_parameter_object : dict A CTSimU parameter structure, as imported from a JSON scenario description. Returns ------- success : bool `True` on success, `False` if an error occurred. """ return self.amount.set_from_json(json_parameter_object) def set_from_json(self, json_deviation_object:dict) -> bool: """Set the deviation from a CTSimU JSON deviation structure. Parameters ---------- json_deviation_object : dict A CTSimU deviation structure, as imported from a JSON scenario description. Returns ------- success : bool `True` on success, `False` if an error occurred. Raises ------ Exception When an error occurred. """ if json_exists_and_not_null(json_deviation_object, ["type"]): self.set_type(get_value(json_deviation_object, ["type"])) else: raise Exception('CTSimU Deviation: set_from_json: a geometrical deviation must provide a "type": either "rotation" or "translation".') return False # Transformation axis: if json_exists_and_not_null(json_deviation_object, ["axis"]): axis_obj = json_extract(json_deviation_object, ["axis"]) if isinstance(axis_obj, str): # Axis is given as a single string. if axis_obj in ctsimu_axis_strings: self.set_axis(axis_obj) else: raise Exception(f'CTSimU Deviation: set_from_json: The deviation "axis" string is incorrect: must be any of {ctsimu_axis_strings} or a free vector definition.') return False elif isinstance(axis_obj, dict): # Free vector definition. if self.axis.set_from_json(axis_obj): # Success. pass else: raise Exception(f'CTSimU Deviation: set_from_json: Failed to set up deviation axis from JSON file. Vector definition seems to be incorrect.') return False else: raise Exception("CTSimU Deviation: set_from_json: Failed to set up deviation axis from JSON structure.") # Pivot point for rotations: # Set a standard pivot which refers to the object's center: self.pivot.set_simple(0, 0, 0) # Set "sample" as the "most local" pivot reference. # Will be converted to "local" if necessary: self.pivot.set_reference("sample") if json_exists_and_not_null(json_deviation_object, ["pivot"]): # If another pivot is defined in the # JSON file, take this one instead... if self.pivot.set_from_json(json_extract(json_deviation_object, ["pivot"])): # Success pass else: raise Exception("CTSimU Deviation: set_from_json: Failed to set up deviation's pivot point from JSON structure. Vector definition seems to be incorrect.") return False self.set_amount_from_json(json_extract(json_deviation_object, ["amount"])) self.set_known_to_reconstruction(get_value_in_native_unit( native_unit="bool", dictionary=json_deviation_object, keys=["known_to_reconstruction"], fail_value=True )) return True def json_dict(self) -> dict: """Create a dictionary of this deviation for a CTSimU JSON file.""" jd = dict() jd["type"] = self.type jd["axis"] = self.axis.json_dict() if self.type == "rotation": jd["pivot"] = self.pivot.json_dict() jd["amount"] = self.amount.json_dict() jd["known_to_reconstruction"] = self.known_to_reconstruction return jd def deviate(self, coordinate_system:'CoordinateSystem', frame:float, n_frames:int, reconstruction=False, attached_to_stage:bool=False, stage_coordinate_system:'CoordinateSystem'=None) -> 'CoordinateSystem': """ Apply this deviation to the given coordinate system. This function checks if the deviation's `known_to_reconstruction` property applies to this case, Parameters ---------- coordinate_system : ctsimu.geometry.CoordinateSystem The coordinate system that will be deviated. frame : float Frame number. n_frames : int Total number of frames in scan. reconstruction : bool If `True`, the deviation is only applied if it is known to the reconstruction software. The same applies to whether its drifts are considered or not. If `False`, the full deviation and all of its drifts will be applied to the coordinate system. attached_to_stage : bool `True` if the coordinate system is attached to the sample stage, `False` if its reference coordinate system is a world coordinate system. stage_coordinate_system : ctsimu.geometry.CoordinateSystem, optional The stage coordinate system. Only necessary if the part is attached to the sample stage. Otherwise, `None` can be passed. Returns ------- deviated_cs : ctsimu.geometry.CoordinateSystem Coordinate system with the applied deviation. Note that the original `coordinate_system` passed as an argument will be altered equally. """ if (self.known_to_reconstruction is True) or (reconstruction is False): amount = self.amount.set_frame_and_get_value( frame=frame, n_frames=n_frames, reconstruction=reconstruction ) if self.type == "translation": if self.amount.native_unit == "mm": if attached_to_stage is False: # Object in world coordinate system. # -------------------------------------- # The deviation axis can undergo drifts and could # be expressed in any coordinate system (world, local, sample). # Therefore, the axis is a `Scenevector`, which can # give us the translation vector for the current frame: translation_axis = self.axis.direction_in_world( local=coordinate_system, sample=ctsimu_world, frame=frame, n_frames=n_frames, reconstruction=reconstruction ) coordinate_system.translate_in_direction(direction=translation_axis, distance=amount) else: # Object is in stage coordinate system. # -------------------------------------- translation_axis = self.axis.direction_in_local( local=stage_coordinate_system, sample=coordinate_system, frame=frame, n_frames=n_frames, reconstruction=reconstruction ) coordinate_system.translate_in_direction(direction=translation_axis, distance=amount) else: raise Exception(f"CTSimU Deviation: deviate: Translational deviation must be given in a unit of length. The given unit '{self.amount.native_unit}' cannot be used for a translation.") elif self.type == "rotation": if self.amount.native_unit == "rad": if attached_to_stage is False: # Object in world coordinate system. # -------------------------------------- rotation_axis = self.axis.direction_in_world( local=coordinate_system, sample=ctsimu_world, frame=frame, n_frames=n_frames, reconstruction=reconstruction ) pivot_point = self.pivot.point_in_world( local=coordinate_system, sample=ctsimu_world, frame=frame, n_frames=n_frames, reconstruction=reconstruction ) coordinate_system.rotate_around_pivot_point( axis=rotation_axis, angle=amount, pivot=pivot_point ) else: # Object is in stage coordinate system. # -------------------------------------- rotation_axis = self.axis.direction_in_local( local=stage_coordinate_system, sample=coordinate_system, frame=frame, n_frames=n_frames, reconstruction=reconstruction ) pivot_point = self.pivot.point_in_local( local=stage_coordinate_system, sample=coordinate_system, frame=frame, n_frames=n_frames, reconstruction=reconstruction ) coordinate_system.rotate_around_pivot_point( axis=rotation_axis, angle=amount, pivot=pivot_point ) else: raise Exception(f"CTSimU Deviation: deviate: Rotational deviation must be given in an angular unit. The given unit '{self.amount.native_unit}' cannot be used for a rotation.") return coordinate_system
Methods
def deviate(self, coordinate_system: CoordinateSystem, frame: float, n_frames: int, reconstruction=False, attached_to_stage: bool = False, stage_coordinate_system: CoordinateSystem = None) ‑> CoordinateSystem
-
Apply this deviation to the given coordinate system.
This function checks if the deviation's
known_to_reconstruction
property applies to this case,Parameters
coordinate_system
:CoordinateSystem
- The coordinate system that will be deviated.
frame
:float
- Frame number.
n_frames
:int
- Total number of frames in scan.
reconstruction
:bool
- If
True
, the deviation is only applied if it is known to the reconstruction software. The same applies to whether its drifts are considered or not. IfFalse
, the full deviation and all of its drifts will be applied to the coordinate system. attached_to_stage
:bool
True
if the coordinate system is attached to the sample stage,False
if its reference coordinate system is a world coordinate system.stage_coordinate_system
:CoordinateSystem
, optional- The stage coordinate system. Only necessary if the part is attached
to the sample stage. Otherwise,
None
can be passed.
Returns
deviated_cs
:CoordinateSystem
- Coordinate system with the applied deviation. Note that the
original
coordinate_system
passed as an argument will be altered equally.
Expand source code
def deviate(self, coordinate_system:'CoordinateSystem', frame:float, n_frames:int, reconstruction=False, attached_to_stage:bool=False, stage_coordinate_system:'CoordinateSystem'=None) -> 'CoordinateSystem': """ Apply this deviation to the given coordinate system. This function checks if the deviation's `known_to_reconstruction` property applies to this case, Parameters ---------- coordinate_system : ctsimu.geometry.CoordinateSystem The coordinate system that will be deviated. frame : float Frame number. n_frames : int Total number of frames in scan. reconstruction : bool If `True`, the deviation is only applied if it is known to the reconstruction software. The same applies to whether its drifts are considered or not. If `False`, the full deviation and all of its drifts will be applied to the coordinate system. attached_to_stage : bool `True` if the coordinate system is attached to the sample stage, `False` if its reference coordinate system is a world coordinate system. stage_coordinate_system : ctsimu.geometry.CoordinateSystem, optional The stage coordinate system. Only necessary if the part is attached to the sample stage. Otherwise, `None` can be passed. Returns ------- deviated_cs : ctsimu.geometry.CoordinateSystem Coordinate system with the applied deviation. Note that the original `coordinate_system` passed as an argument will be altered equally. """ if (self.known_to_reconstruction is True) or (reconstruction is False): amount = self.amount.set_frame_and_get_value( frame=frame, n_frames=n_frames, reconstruction=reconstruction ) if self.type == "translation": if self.amount.native_unit == "mm": if attached_to_stage is False: # Object in world coordinate system. # -------------------------------------- # The deviation axis can undergo drifts and could # be expressed in any coordinate system (world, local, sample). # Therefore, the axis is a `Scenevector`, which can # give us the translation vector for the current frame: translation_axis = self.axis.direction_in_world( local=coordinate_system, sample=ctsimu_world, frame=frame, n_frames=n_frames, reconstruction=reconstruction ) coordinate_system.translate_in_direction(direction=translation_axis, distance=amount) else: # Object is in stage coordinate system. # -------------------------------------- translation_axis = self.axis.direction_in_local( local=stage_coordinate_system, sample=coordinate_system, frame=frame, n_frames=n_frames, reconstruction=reconstruction ) coordinate_system.translate_in_direction(direction=translation_axis, distance=amount) else: raise Exception(f"CTSimU Deviation: deviate: Translational deviation must be given in a unit of length. The given unit '{self.amount.native_unit}' cannot be used for a translation.") elif self.type == "rotation": if self.amount.native_unit == "rad": if attached_to_stage is False: # Object in world coordinate system. # -------------------------------------- rotation_axis = self.axis.direction_in_world( local=coordinate_system, sample=ctsimu_world, frame=frame, n_frames=n_frames, reconstruction=reconstruction ) pivot_point = self.pivot.point_in_world( local=coordinate_system, sample=ctsimu_world, frame=frame, n_frames=n_frames, reconstruction=reconstruction ) coordinate_system.rotate_around_pivot_point( axis=rotation_axis, angle=amount, pivot=pivot_point ) else: # Object is in stage coordinate system. # -------------------------------------- rotation_axis = self.axis.direction_in_local( local=stage_coordinate_system, sample=coordinate_system, frame=frame, n_frames=n_frames, reconstruction=reconstruction ) pivot_point = self.pivot.point_in_local( local=stage_coordinate_system, sample=coordinate_system, frame=frame, n_frames=n_frames, reconstruction=reconstruction ) coordinate_system.rotate_around_pivot_point( axis=rotation_axis, angle=amount, pivot=pivot_point ) else: raise Exception(f"CTSimU Deviation: deviate: Rotational deviation must be given in an angular unit. The given unit '{self.amount.native_unit}' cannot be used for a rotation.") return coordinate_system
def has_drifts(self) ‑> bool
-
Does the deviation specify any drift?
Returns
has_drifts
:bool
True
if the deviation specifies any drift, otherwiseFalse
.
Expand source code
def has_drifts(self) -> bool: """Does the deviation specify any drift? Returns ------- has_drifts : bool `True` if the deviation specifies any drift, otherwise `False`. """ return self.amount.has_drifts() or self.axis.has_drifts() or self.pivot.has_drifts()
def json_dict(self) ‑> dict
-
Create a dictionary of this deviation for a CTSimU JSON file.
Expand source code
def json_dict(self) -> dict: """Create a dictionary of this deviation for a CTSimU JSON file.""" jd = dict() jd["type"] = self.type jd["axis"] = self.axis.json_dict() if self.type == "rotation": jd["pivot"] = self.pivot.json_dict() jd["amount"] = self.amount.json_dict() jd["known_to_reconstruction"] = self.known_to_reconstruction return jd
def set_amount_from_json(self, json_parameter_object: dict) ‑> bool
-
Set the deviation's amount from a JSON object, which is a parameter with a value and potentially a drift.
This function is usually not called from the outside, but used by
set_from_json
.Parameters
json_parameter_object
:dict
- A CTSimU parameter structure, as imported from a JSON scenario description.
Returns
success
:bool
True
on success,False
if an error occurred.
Expand source code
def set_amount_from_json(self, json_parameter_object:dict) -> bool: """Set the deviation's amount from a JSON object, which is a parameter with a value and potentially a drift. This function is usually not called from the outside, but used by `set_from_json`. Parameters ---------- json_parameter_object : dict A CTSimU parameter structure, as imported from a JSON scenario description. Returns ------- success : bool `True` on success, `False` if an error occurred. """ return self.amount.set_from_json(json_parameter_object)
def set_axis(self, axis)
-
Set the transformation axis. Can be a string identifier:
"x"
,"y"
,"z"
(for world axes),"u"
,"v"
,"w"
(for local axes),"r"
,"s"
,"t"
(for sample axes), or an arbitraryScenevector
object.Parameters
axis
:str
orScenevector
- Transformation axis.
Expand source code
def set_axis(self, axis): """Set the transformation axis. Can be a string identifier: `"x"`, `"y"`, `"z"` (for world axes), `"u"`, `"v"`, `"w"` (for local axes), `"r"`, `"s"`, `"t"` (for sample axes), or an arbitrary `ctsimu.scenario.scenevector.Scenevector` object. Parameters ---------- axis : str or ctsimu.scenario.scenevector.Scenevector Transformation axis. """ if isinstance(axis, str): if axis in ctsimu_world_axis_designations: # Given axis is "x", "y" or "z" # -> vector in world coordinate system self.axis.set_reference("world") if axis == "x": self.axis.set_simple(1, 0, 0) if axis == "y": self.axis.set_simple(0, 1, 0) if axis == "z": self.axis.set_simple(0, 0, 1) elif axis in ctsimu_local_axis_designations: # Given axis is "u", "v" or "w" # -> vector in local coordinate system self.axis.set_reference("local") if axis == "u": self.axis.set_simple(1, 0, 0) if axis == "v": self.axis.set_simple(0, 1, 0) if axis == "w": self.axis.set_simple(0, 0, 1) elif axis in ctsimu_sample_axis_designations: # Given axis is "r", "s" or "t" # -> vector in sample coordinate system self.axis.set_reference("sample") if axis == "r": self.axis.set_simple(1, 0, 0) if axis == "s": self.axis.set_simple(0, 1, 0) if axis == "t": self.axis.set_simple(0, 0, 1) else: raise Exception(f"CTSimU Deviation: set_axis: Not a valid axis designation string: '{axis}'. Valid options are: {ctsimu_axis_strings}.") elif isinstance(axis, Scenevector): self.axis = axis else: raise Exception(f"CTSimU Deviation: set_axis: Not a valid deviation axis type: '{type(axis)}'. Valid options are: str, Scenevector.")
def set_from_json(self, json_deviation_object: dict) ‑> bool
-
Set the deviation from a CTSimU JSON deviation structure.
Parameters
json_deviation_object
:dict
- A CTSimU deviation structure, as imported from a JSON scenario description.
Returns
success
:bool
True
on success,False
if an error occurred.
Raises
Exception
- When an error occurred.
Expand source code
def set_from_json(self, json_deviation_object:dict) -> bool: """Set the deviation from a CTSimU JSON deviation structure. Parameters ---------- json_deviation_object : dict A CTSimU deviation structure, as imported from a JSON scenario description. Returns ------- success : bool `True` on success, `False` if an error occurred. Raises ------ Exception When an error occurred. """ if json_exists_and_not_null(json_deviation_object, ["type"]): self.set_type(get_value(json_deviation_object, ["type"])) else: raise Exception('CTSimU Deviation: set_from_json: a geometrical deviation must provide a "type": either "rotation" or "translation".') return False # Transformation axis: if json_exists_and_not_null(json_deviation_object, ["axis"]): axis_obj = json_extract(json_deviation_object, ["axis"]) if isinstance(axis_obj, str): # Axis is given as a single string. if axis_obj in ctsimu_axis_strings: self.set_axis(axis_obj) else: raise Exception(f'CTSimU Deviation: set_from_json: The deviation "axis" string is incorrect: must be any of {ctsimu_axis_strings} or a free vector definition.') return False elif isinstance(axis_obj, dict): # Free vector definition. if self.axis.set_from_json(axis_obj): # Success. pass else: raise Exception(f'CTSimU Deviation: set_from_json: Failed to set up deviation axis from JSON file. Vector definition seems to be incorrect.') return False else: raise Exception("CTSimU Deviation: set_from_json: Failed to set up deviation axis from JSON structure.") # Pivot point for rotations: # Set a standard pivot which refers to the object's center: self.pivot.set_simple(0, 0, 0) # Set "sample" as the "most local" pivot reference. # Will be converted to "local" if necessary: self.pivot.set_reference("sample") if json_exists_and_not_null(json_deviation_object, ["pivot"]): # If another pivot is defined in the # JSON file, take this one instead... if self.pivot.set_from_json(json_extract(json_deviation_object, ["pivot"])): # Success pass else: raise Exception("CTSimU Deviation: set_from_json: Failed to set up deviation's pivot point from JSON structure. Vector definition seems to be incorrect.") return False self.set_amount_from_json(json_extract(json_deviation_object, ["amount"])) self.set_known_to_reconstruction(get_value_in_native_unit( native_unit="bool", dictionary=json_deviation_object, keys=["known_to_reconstruction"], fail_value=True )) return True
def set_known_to_reconstruction(self, known_to_reconstruction: bool)
-
Sets the
known_to_reconstruction
attribute.Parameters
known_to_reconstruction
:bool
- Should this geometrical deviation be considered by the reconstruction software? This attribute is obeyed when calculating projection matrices.
Expand source code
def set_known_to_reconstruction(self, known_to_reconstruction:bool): """Sets the `known_to_reconstruction` attribute. Parameters ---------- known_to_reconstruction : bool Should this geometrical deviation be considered by the reconstruction software? This attribute is obeyed when calculating projection matrices. """ if known_to_reconstruction == True: self.known_to_reconstruction = True else: self.known_to_reconstruction = False
def set_pivot(self, pivot: Scenevector)
-
Set the pivot point for rotations.
Parameters
pivot
:Scenevector
Expand source code
def set_pivot(self, pivot:'Scenevector'): """Set the pivot point for rotations. Parameters ---------- pivot : Scenevector """ if isinstance(pivot, Scenevector): self.pivot = pivot else: raise Exception(f"CTSimU Deviation: set_pivot: expects an instance of 'Scenevector', but given pivot is of type {type(pivot)}.")
def set_type(self, transformation_type)
-
Set the transformation type.
Parameters
transformation_type
:str
- Transformation type.
"translation"
or"rotation"
.
Expand source code
def set_type(self, transformation_type): """Set the transformation type. Parameters ---------- transformation_type : str Transformation type. `"translation"` or `"rotation"`. """ if (transformation_type == "rotation") or (transformation_type == "translation"): self.type = transformation_type # Set the correct native unit for the amount: if self.type == "rotation": self.amount.set_native_unit("rad") elif self.type == "translation": self.amount.set_native_unit("mm") else: raise Exception(f"CTSimU Deviation: set_type: Not a valid transformation type: '{transformation_type}'. Valid types are 'translation' and 'rotation'.")