Skip to content

Commit 4a59f05

Browse files
smereupyansys-ci-botRobPasMuejacobrkerstetterpre-commit-ci[bot]
authored
feat: tracking boolean operations (#2153)
Co-authored-by: PyAnsys CI Bot <[email protected]> Co-authored-by: Roberto Pastor Muela <[email protected]> Co-authored-by: Jacob Kerstetter <[email protected]> Co-authored-by: Jacob Kerstetter <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent b94575b commit 4a59f05

File tree

15 files changed

+620
-350
lines changed

15 files changed

+620
-350
lines changed

doc/changelog.d/2153.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Tracking boolean operations

src/ansys/geometry/core/_grpc/_services/base/bodies.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,9 @@ def boolean(self, **kwargs) -> dict:
220220
pass
221221

222222
@abstractmethod
223+
def combine(self, **kwargs) -> dict:
224+
"""Boolean operation through command."""
225+
223226
def split_body(self, **kwargs) -> dict:
224227
"""Split a body."""
225228
pass

src/ansys/geometry/core/_grpc/_services/base/repair_tools.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,53 @@ def inspect_geometry(self, **kwargs) -> dict:
128128
def repair_geometry(self, **kwargs) -> dict:
129129
"""Repair the geometry by addressing identified issues."""
130130
pass
131+
132+
@abstractmethod
133+
def fix_duplicate_faces(self, **kwargs) -> dict: # noqa: D102
134+
"""Fix duplicate faces in the geometry."""
135+
pass
136+
137+
@abstractmethod
138+
def fix_missing_faces(self, **kwargs) -> dict: # noqa: D102
139+
"""Fix missing faces in the geometry."""
140+
pass
141+
142+
@abstractmethod
143+
def fix_inexact_edges(self, **kwargs) -> dict: # noqa: D102
144+
"""Fix inexact edges in the geometry."""
145+
pass
146+
147+
@abstractmethod
148+
def fix_extra_edges(self, **kwargs) -> dict: # noqa: D102
149+
"""Fix extra edges in the geometry."""
150+
pass
151+
152+
@abstractmethod
153+
def fix_short_edges(self, **kwargs) -> dict: # noqa: D102
154+
"""Fix short edges in the geometry."""
155+
pass
156+
157+
@abstractmethod
158+
def fix_small_faces(self, **kwargs) -> dict: # noqa: D102
159+
"""Fix small faces in the geometry."""
160+
pass
161+
162+
@abstractmethod
163+
def fix_split_edges(self, **kwargs) -> dict: # noqa: D102
164+
"""Fix split edges in the geometry."""
165+
pass
166+
167+
@abstractmethod
168+
def fix_stitch_faces(self, **kwargs) -> dict: # noqa: D102
169+
"""Fix stitch faces in the geometry."""
170+
pass
171+
172+
@abstractmethod
173+
def fix_unsimplified_faces(self, **kwargs) -> dict: # noqa: D102
174+
"""Fix areas to simplify in the geometry."""
175+
pass
176+
177+
@abstractmethod
178+
def fix_interference(self, **kwargs) -> dict: # noqa: D102
179+
"""Fix interferences in the geometry."""
180+
pass

src/ansys/geometry/core/_grpc/_services/v0/bodies.py

Lines changed: 76 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import grpc
2525
import pint
2626

27+
import ansys.geometry.core as pyansys_geom
2728
from ansys.geometry.core.errors import protect_grpc
2829
from ansys.geometry.core.misc.measurements import DEFAULT_UNITS
2930

@@ -43,6 +44,7 @@
4344
from_trimmed_curve_to_grpc_trimmed_curve,
4445
from_trimmed_surface_to_grpc_trimmed_surface,
4546
from_unit_vector_to_grpc_direction,
47+
serialize_tracker_command_response,
4648
)
4749

4850

@@ -714,15 +716,22 @@ def boolean(self, **kwargs) -> dict: # noqa: D102
714716
from ansys.api.geometry.v0.bodies_pb2 import BooleanRequest
715717

716718
# Call the gRPC service and build the requests accordingly
717-
resp = 0
719+
response_success = 0
720+
serialized_tracker_response = {}
718721
try:
719-
resp = self.stub.Boolean(
720-
request=BooleanRequest(
721-
body1=kwargs["target"].id,
722-
tool_bodies=[other.id for other in kwargs["other"]],
723-
method=kwargs["method"],
722+
request = BooleanRequest(
723+
body1=kwargs["target"].id,
724+
tool_bodies=[other.id for other in kwargs["other"]],
725+
method=kwargs["method"],
726+
)
727+
if pyansys_geom.USE_TRACKER_TO_UPDATE_DESIGN:
728+
request.keep_other = kwargs["keep_other"]
729+
resp = self.stub.Boolean(request=request)
730+
response_success = resp.empty_result
731+
if pyansys_geom.USE_TRACKER_TO_UPDATE_DESIGN:
732+
serialized_tracker_response = serialize_tracker_command_response(
733+
response=resp.response
724734
)
725-
).empty_result
726735
except grpc.RpcError as err: # pragma: no cover
727736
# TODO: to be deleted - old versions did not have "tool_bodies" in the request
728737
# This is a temporary fix to support old versions of the server - should be deleted
@@ -741,26 +750,82 @@ def boolean(self, **kwargs) -> dict: # noqa: D102
741750
all_resp.append(tmp_resp)
742751

743752
if all_resp.count(1) > 0:
744-
resp = 1
753+
response_success = 1
745754
elif len(kwargs["other"]) == 1:
746755
resp = self.stub.Boolean(
747756
request=BooleanRequest(
748757
body1=kwargs["target"].id,
749758
body2=kwargs["other"][0].id,
750759
method=kwargs["method"],
751760
)
752-
).empty_result
761+
)
762+
response_success = resp.empty_result
753763
else:
754764
raise err
755765

756-
if resp == 1:
766+
if response_success == 1:
757767
raise ValueError(
758768
f"Boolean operation of type '{kwargs['method']}' failed: {kwargs['err_msg']}.\n"
759769
f"Involving bodies:{kwargs['target']}, {kwargs['other']}"
760770
)
761771

762772
# Return the response - formatted as a dictionary
763-
return {}
773+
return {"complete_command_response": serialized_tracker_response}
774+
775+
@protect_grpc
776+
def combine(self, **kwargs) -> dict: # noqa: D102
777+
from ansys.api.geometry.v0.bodies_pb2 import (
778+
CombineIntersectBodiesRequest,
779+
CombineMergeBodiesRequest,
780+
)
781+
782+
other_bodies = kwargs["other"]
783+
type_bool_op = kwargs["type_bool_op"]
784+
keep_other = kwargs["keep_other"]
785+
786+
if type_bool_op == "intersect":
787+
body_ids = [body._grpc_id for body in other_bodies]
788+
target_ids = [self._grpc_id]
789+
request = CombineIntersectBodiesRequest(
790+
target_selection=target_ids,
791+
tool_selection=body_ids,
792+
subtract_from_target=False,
793+
keep_cutter=keep_other,
794+
)
795+
response = self._template._commands_stub.CombineIntersectBodies(request)
796+
elif type_bool_op == "subtract":
797+
body_ids = [body._grpc_id for body in other_bodies]
798+
target_ids = [self._grpc_id]
799+
request = CombineIntersectBodiesRequest(
800+
target_selection=target_ids,
801+
tool_selection=body_ids,
802+
subtract_from_target=True,
803+
keep_cutter=keep_other,
804+
)
805+
response = self._template._commands_stub.CombineIntersectBodies(request)
806+
elif type_bool_op == "unite":
807+
bodies = [self]
808+
bodies.extend(other_bodies)
809+
body_ids = [body._grpc_id for body in bodies]
810+
request = CombineMergeBodiesRequest(target_selection=body_ids)
811+
response = self._template._commands_stub.CombineMergeBodies(request)
812+
else:
813+
raise ValueError("Unknown operation requested")
814+
if not response.success:
815+
raise ValueError(
816+
f"Operation of type '{type_bool_op}' failed: {kwargs['err_msg']}.\n"
817+
f"Involving bodies:{self}, {other_bodies}"
818+
)
819+
820+
if not keep_other:
821+
for b in other_bodies:
822+
b.parent_component.delete_body(b)
823+
824+
tracker_response = response.result.complete_command_response
825+
serialized_tracker_response = serialize_tracker_command_response(response=tracker_response)
826+
827+
# Return the response - formatted as a dictionary
828+
return {"complete_command_response": serialized_tracker_response}
764829

765830
@protect_grpc
766831
def split_body(self, **kwargs) -> dict: # noqa: D102

src/ansys/geometry/core/_grpc/_services/v0/components.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ def create(self, **kwargs) -> dict: # noqa: D102
7474
"id": response.component.id,
7575
"name": response.component.name,
7676
"instance_name": response.component.instance_name,
77+
"template": response.template,
78+
"component": response.component,
7779
}
7880

7981
@protect_grpc

src/ansys/geometry/core/_grpc/_services/v0/conversions.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1329,3 +1329,55 @@ def _check_write_body_facets_input(backend_version: "semver.Version", write_body
13291329
+ "26.1.0, but the current version used is "
13301330
+ f"{backend_version}."
13311331
)
1332+
1333+
1334+
def serialize_tracker_command_response(**kwargs) -> dict:
1335+
"""Serialize a TrackerCommandResponse object into a dictionary.
1336+
1337+
Parameters
1338+
----------
1339+
response : TrackerCommandResponse
1340+
The gRPC TrackerCommandResponse object to serialize.
1341+
1342+
Returns
1343+
-------
1344+
dict
1345+
A dictionary representation of the TrackerCommandResponse object.
1346+
"""
1347+
1348+
def serialize_body(body):
1349+
return {
1350+
"id": body.id,
1351+
"name": body.name,
1352+
"can_suppress": body.can_suppress,
1353+
"transform_to_master": {
1354+
"m00": body.transform_to_master.m00,
1355+
"m11": body.transform_to_master.m11,
1356+
"m22": body.transform_to_master.m22,
1357+
"m33": body.transform_to_master.m33,
1358+
},
1359+
"master_id": body.master_id,
1360+
"parent_id": body.parent_id,
1361+
"is_surface": body.is_surface,
1362+
}
1363+
1364+
def serialize_entity_identifier(entity):
1365+
"""Serialize an EntityIdentifier object into a dictionary."""
1366+
return {
1367+
"id": entity.id,
1368+
}
1369+
1370+
response = kwargs["response"]
1371+
return {
1372+
"success": response.success,
1373+
"created_bodies": [
1374+
serialize_body(body) for body in getattr(response, "created_bodies", [])
1375+
],
1376+
"modified_bodies": [
1377+
serialize_body(body) for body in getattr(response, "modified_bodies", [])
1378+
],
1379+
"deleted_bodies": [
1380+
serialize_entity_identifier(entity)
1381+
for entity in getattr(response, "deleted_bodies", [])
1382+
],
1383+
}

0 commit comments

Comments
 (0)