diff --git a/src/ansys/acp/core/_tree_objects/linked_selection_rule.py b/src/ansys/acp/core/_tree_objects/linked_selection_rule.py index c114076f5b..10433b79af 100644 --- a/src/ansys/acp/core/_tree_objects/linked_selection_rule.py +++ b/src/ansys/acp/core/_tree_objects/linked_selection_rule.py @@ -10,6 +10,7 @@ from ._grpc_helpers.edge_property_list import GenericEdgePropertyType from ._grpc_helpers.polymorphic_from_pb import tree_object_from_resource_path from .base import CreatableTreeObject +from .cutoff_selection_rule import CutoffSelectionRule from .cylindrical_selection_rule import CylindricalSelectionRule from .enums import ( BooleanOperationType, @@ -28,13 +29,14 @@ from .boolean_selection_rule import BooleanSelectionRule _LINKABLE_SELECTION_RULE_TYPES = Union[ - ParallelSelectionRule, + BooleanSelectionRule, + CutoffSelectionRule, CylindricalSelectionRule, + GeometricalSelectionRule, + ParallelSelectionRule, SphericalSelectionRule, TubeSelectionRule, - GeometricalSelectionRule, VariableOffsetSelectionRule, - BooleanSelectionRule, ] @@ -74,7 +76,8 @@ class LinkedSelectionRule(GenericEdgePropertyType): :class:`.BooleanSelectionRule` \- \- ====================================== ================================== =================== - Note that ``CutoffSelectionRule`` and ``BooleanSelectionRule`` objects cannot be linked. + Note that :class:`.CutoffSelectionRule` and :class:`.BooleanSelectionRule` objects cannot be linked to + a Boolean Selection Rule, only to a Modeling Ply. """ def __init__( @@ -85,12 +88,16 @@ def __init__( parameter_1: float = 0.0, parameter_2: float = 0.0, ): - self._selection_rule = selection_rule - self._operation_type = operation_type - self._template_rule = template_rule - self._parameter_1 = parameter_1 - self._parameter_2 = parameter_2 + # The '_callback_apply_changes' needs to be set first, otherwise the + # setter methods will not work. We go through the setters instead of + # directly setting the attributes to ensure the setter validation is + # performed. self._callback_apply_changes: Callable[[], None] | None = None + self.selection_rule = selection_rule + self.operation_type = operation_type + self.template_rule = template_rule + self.parameter_1 = parameter_1 + self.parameter_2 = parameter_2 @property def selection_rule(self) -> _LINKABLE_SELECTION_RULE_TYPES: @@ -110,6 +117,15 @@ def operation_type(self) -> BooleanOperationType: @operation_type.setter def operation_type(self, value: BooleanOperationType) -> None: + # The backend converts the operation automatically; this is confusing + # in the scripting context where the associated warning may not be visible. + if isinstance(self._selection_rule, CutoffSelectionRule): + if value != BooleanOperationType.INTERSECT: + raise ValueError( + "Cannot use a boolean operation other than 'INTERSECT' with a " + "CutoffSelectionRule." + ) + self._operation_type = value if self._callback_apply_changes is not None: self._callback_apply_changes() @@ -160,19 +176,20 @@ def _from_pb_object( from .boolean_selection_rule import BooleanSelectionRule # Cannot link to objects of the same type as the parent. - allowed_types = tuple( - type_ - for type_ in [ - ParallelSelectionRule, - CylindricalSelectionRule, - SphericalSelectionRule, - TubeSelectionRule, - GeometricalSelectionRule, - VariableOffsetSelectionRule, + allowed_types_list = [ + ParallelSelectionRule, + CylindricalSelectionRule, + SphericalSelectionRule, + TubeSelectionRule, + GeometricalSelectionRule, + VariableOffsetSelectionRule, + ] + if not isinstance(parent_object, BooleanSelectionRule): + allowed_types_list += [ + CutoffSelectionRule, BooleanSelectionRule, ] - if not isinstance(parent_object, type_) - ) + allowed_types = tuple(allowed_types_list) selection_rule = tree_object_from_resource_path( resource_path=message.resource_path, channel=parent_object._channel diff --git a/tests/unittests/test_modeling_ply.py b/tests/unittests/test_modeling_ply.py index d1066ac5e7..86f2faff43 100644 --- a/tests/unittests/test_modeling_ply.py +++ b/tests/unittests/test_modeling_ply.py @@ -5,6 +5,7 @@ from ansys.acp.core import ( BooleanOperationType, + CutoffSelectionRule, DrapingType, ElementalDataType, Fabric, @@ -121,6 +122,13 @@ def object_properties(request, parent_model): parameter_1=0.0, parameter_2=0.0, ), + LinkedSelectionRule( + selection_rule=parent_model.create_cutoff_selection_rule(), + operation_type=BooleanOperationType.INTERSECT, + template_rule=True, + parameter_1=1.2, + parameter_2=3.9, + ), LinkedSelectionRule( selection_rule=parent_model.create_boolean_selection_rule(), operation_type=BooleanOperationType.REMOVE, @@ -397,3 +405,16 @@ def test_linked_selection_rule_parameters(simple_modeling_ply, minimal_complete_ linked_rule_in_ply = simple_modeling_ply.selection_rules[0] linked_rule_in_ply.parameter_1 = 1 assert linked_parallel_rule.parameter_1 == simple_modeling_ply.selection_rules[0].parameter_1 + + +@pytest.mark.parametrize( + "operation_type", [e for e in BooleanOperationType if e != BooleanOperationType.INTERSECT] +) +def test_linked_cutoff_selection_rule_operation_type(operation_type): + """Check that CutoffSelectionRule only allows INTERSECT operation type.""" + with pytest.raises(ValueError) as exc: + LinkedSelectionRule( + selection_rule=CutoffSelectionRule(), # type: ignore + operation_type=operation_type, + ) + assert "INTERSECT" in str(exc.value)