Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
97eb31a
chore: update CHANGELOG for v0.9.0 (#1753)
pyansys-ci-bot Feb 17, 2025
1547839
Merge branch 'main' of https://github.com/ansys/pyansys-geometry
smereu Mar 10, 2025
5a48878
Merge branch 'main' of https://github.com/ansys/pyansys-geometry
smereu Mar 27, 2025
4109456
Merge branch 'main' of https://github.com/ansys/pyansys-geometry
smereu Apr 8, 2025
e00316b
Merge branch 'main' of https://github.com/ansys/pyansys-geometry
smereu May 20, 2025
1afa37a
Merge branch 'main' of https://github.com/ansys/pyansys-geometry
smereu Jun 11, 2025
7c595bb
Merge branch 'main' of https://github.com/ansys/pyansys-geometry
smereu Jun 24, 2025
6a23076
Merge branch 'main' of https://github.com/ansys/pyansys-geometry
smereu Jul 7, 2025
62ca792
Merge branch 'main' of https://github.com/ansys/pyansys-geometry
smereu Jul 9, 2025
40c19c1
Merge branch 'main' of https://github.com/ansys/pyansys-geometry
smereu Jul 28, 2025
2952b14
finalize tracker changes for boolean and update from response
smereu Aug 5, 2025
aacd11a
chore: adding changelog file 2153.added.md [dependabot-skip]
pyansys-ci-bot Aug 11, 2025
b3b82b8
Merge branch 'main' into feat/track_boolean_operations
RobPasMue Aug 20, 2025
8344d41
chore: adding changelog file 2153.added.md [dependabot-skip]
pyansys-ci-bot Aug 20, 2025
12b13c0
Merge branch 'main' into feat/track_boolean_operations
jacobrkerstetter Aug 25, 2025
15176d3
Merge branch 'main' into feat/track_boolean_operations
jacobrkerstetter Sep 3, 2025
e172e45
Delete doc/changelog.d/1753.maintenance.md
RobPasMue Sep 3, 2025
1e711f6
fixing some comments
jacobrkerstetter Sep 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/changelog.d/2153.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Tracking boolean operations
27 changes: 18 additions & 9 deletions src/ansys/geometry/core/_grpc/_services/v0/bodies.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
import grpc
import pint

import ansys.geometry.core as pyansys_geom
from ansys.geometry.core.errors import protect_grpc
from ansys.geometry.core.misc.auxiliary import get_design_from_body
from ansys.geometry.core.misc.measurements import DEFAULT_UNITS

from ..base.bodies import GRPCBodyService
Expand Down Expand Up @@ -713,14 +715,21 @@ def boolean(self, **kwargs) -> dict: # noqa: D102

# Call the gRPC service and build the requests accordingly
resp = 0
serialized_tracker_response = {}
try:
resp = self.stub.Boolean(
request=BooleanRequest(
body1=kwargs["target"].id,
tool_bodies=[other.id for other in kwargs["other"]],
method=kwargs["method"],
request = BooleanRequest(
body1=kwargs["target"].id,
tool_bodies=[other.id for other in kwargs["other"]],
method=kwargs["method"],
)
if pyansys_geom.USE_TRACKER_TO_UPDATE_DESIGN:
request.keep_other = kwargs["keep_other"]
resp = self.stub.Boolean(request=request)
if pyansys_geom.USE_TRACKER_TO_UPDATE_DESIGN:
parent_design = get_design_from_body(kwargs["target"])
serialized_tracker_response = parent_design._serialize_tracker_command_response(
resp.response
)
).empty_result
except grpc.RpcError as err: # pragma: no cover
# TODO: to be deleted - old versions did not have "tool_bodies" in the request
# This is a temporary fix to support old versions of the server - should be deleted
Expand All @@ -747,15 +756,15 @@ def boolean(self, **kwargs) -> dict: # noqa: D102
body2=kwargs["other"][0].id,
method=kwargs["method"],
)
).empty_result
)
else:
raise err

if resp == 1:
if resp.empty_result == 1:
raise ValueError(
f"Boolean operation of type '{kwargs['method']}' failed: {kwargs['err_msg']}.\n"
f"Involving bodies:{kwargs['target']}, {kwargs['other']}"
)

# Return the response - formatted as a dictionary
return {}
return {"complete_command_response": serialized_tracker_response}
10 changes: 5 additions & 5 deletions src/ansys/geometry/core/_grpc/_services/v0/repair_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ def find_and_fix_simplify(self, **kwargs) -> dict: # noqa: D102
# Call the gRPC service
response = self.stub.FindAndSimplify(request)

serialized_tracker_response = self._serialize_tracker_command_response(
serialized_tracker_response = kwargs["parent_design"]._serialize_tracker_command_response(
response.complete_command_response
)

Expand Down Expand Up @@ -383,7 +383,7 @@ def find_and_fix_stitch_faces(self, **kwargs) -> dict: # noqa: D102
# Call the gRPC service
response = self.stub.FindAndFixStitchFaces(request)

serialized_tracker_response = self._serialize_tracker_command_response(
serialized_tracker_response = kwargs["parent_design"]._serialize_tracker_command_response(
response.complete_command_response
)

Expand Down Expand Up @@ -467,7 +467,7 @@ def find_and_fix_short_edges(self, **kwargs): # noqa: D102
# Call the gRPC service
response = self.stub.FindAndFixShortEdges(request)

serialized_tracker_response = self._serialize_tracker_command_response(
serialized_tracker_response = kwargs["parent_design"]._serialize_tracker_command_response(
response.complete_command_response
)

Expand All @@ -494,7 +494,7 @@ def find_and_fix_extra_edges(self, **kwargs) -> dict: # noqa: D102
# Call the gRPC service
response = self.stub.FindAndFixExtraEdges(request)

serialized_tracker_response = self._serialize_tracker_command_response(
serialized_tracker_response = kwargs["parent_design"]._serialize_tracker_command_response(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we should align the method used - there is one serialization method as part of this module too. If either one or the other is redundant, we should remove it.

response.complete_command_response
)

Expand Down Expand Up @@ -525,7 +525,7 @@ def find_and_fix_split_edges(self, **kwargs) -> dict: # noqa: D102
# Call the gRPC service
response = self.stub.FindAndFixSplitEdges(request)

serialized_tracker_response = self._serialize_tracker_command_response(
serialized_tracker_response = kwargs["parent_design"]._serialize_tracker_command_response(
response.complete_command_response
)

Expand Down
35 changes: 22 additions & 13 deletions src/ansys/geometry/core/designer/body.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
ShellRequest,
)
from ansys.api.geometry.v0.commands_pb2_grpc import CommandsStub
from ansys.geometry.core import USE_TRACKER_TO_UPDATE_DESIGN
from ansys.geometry.core.connection.client import GrpcClient
from ansys.geometry.core.connection.conversions import (
plane_to_grpc_plane,
Expand Down Expand Up @@ -1962,7 +1963,9 @@ def __generic_boolean_command(
# If USE_TRACKER_TO_UPDATE_DESIGN is True, we serialize the response
# and update the parent design with the serialized response.
tracker_response = response.result.complete_command_response
serialized_response = self._serialize_tracker_command_response(tracker_response)
serialized_response = parent_design._serialize_tracker_command_response(
tracker_response
)
parent_design._update_from_tracker(serialized_response)

@reset_tessellation_cache
Expand All @@ -1976,20 +1979,26 @@ def __generic_boolean_op(
err_msg: str,
) -> None:
grpc_other = other if isinstance(other, Iterable) else [other]
if keep_other:
# Make a copy of the other body to keep it...
# stored temporarily in the parent component - since it will be deleted
grpc_other = [b.copy(self.parent_component, f"BoolOpCopy_{b.name}") for b in grpc_other]

self._template._grpc_client.services.bodies.boolean(
target=self,
other=grpc_other,
method=method,
err_msg=err_msg,
if not USE_TRACKER_TO_UPDATE_DESIGN:
if keep_other:
# Make a copy of the other body to keep it...
# stored temporarily in the parent component - since it will be deleted
grpc_other = [
b.copy(self.parent_component, f"BoolOpCopy_{b.name}") for b in grpc_other
]

response = self._template._grpc_client.services.bodies.boolean(
target=self, other=grpc_other, method=method, err_msg=err_msg, keep_other=keep_other
)

for b in grpc_other:
b.parent_component.delete_body(b)
if not USE_TRACKER_TO_UPDATE_DESIGN:
for b in grpc_other:
b.parent_component.delete_body(b)
else:
# If USE_TRACKER_TO_UPDATE_DESIGN is True, we serialize the response
# and update the parent design with the serialized response.
parent_design = get_design_from_body(self)
parent_design._update_from_tracker(response["complete_command_response"])

def __repr__(self) -> str:
"""Represent the ``Body`` as a string."""
Expand Down
11 changes: 9 additions & 2 deletions src/ansys/geometry/core/designer/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ def __init__(
self._name = new_component.component.name
self._instance_name = new_component.component.instance_name
else:
new_component = None
self._name = name
self._id = None
self._instance_name = instance_name
Expand Down Expand Up @@ -277,8 +278,14 @@ def __init__(

elif not read_existing_comp:
# This is an independent Component - Create new Part and MasterComponent
p = Part(uuid.uuid4(), f"p_{name}", [], [])
master = MasterComponent(uuid.uuid4(), f"master_{name}", p)
p = Part(
uuid.uuid4() if not new_component else new_component.template, f"p_{name}", [], []
)
master = MasterComponent(
uuid.uuid4() if not new_component else new_component.component.master_id,
f"master_{name}",
p,
)
self._master_component = master

self._master_component.occurrences.append(self)
Expand Down
79 changes: 71 additions & 8 deletions src/ansys/geometry/core/designer/design.py
Original file line number Diff line number Diff line change
Expand Up @@ -1045,6 +1045,55 @@ def __repr__(self) -> str:
lines.append(f" N Design Points : {len(self.design_points)}")
return "\n".join(lines)

def _serialize_tracker_command_response(self, response) -> dict:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't go here IMO

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should live inside the "_grpc" layer since it is purely related to the serialization of gRPC responses

"""Serialize a TrackerCommandResponse object into a dictionary.

Parameters
----------
response : TrackerCommandResponse
The gRPC TrackerCommandResponse object to serialize.

Returns
-------
dict
A dictionary representation of the TrackerCommandResponse object.
"""

def serialize_body(body):
return {
"id": body.id,
"name": body.name,
"can_suppress": body.can_suppress,
"transform_to_master": {
"m00": body.transform_to_master.m00,
"m11": body.transform_to_master.m11,
"m22": body.transform_to_master.m22,
"m33": body.transform_to_master.m33,
},
"master_id": body.master_id,
"parent_id": body.parent_id,
}

def serialize_entity_identifier(entity):
"""Serialize an EntityIdentifier object into a dictionary."""
return {
"id": entity.id,
}

return {
"success": response.success,
"created_bodies": [
serialize_body(body) for body in getattr(response, "created_bodies", [])
],
"modified_bodies": [
serialize_body(body) for body in getattr(response, "modified_bodies", [])
],
"deleted_bodies": [
serialize_entity_identifier(entity)
for entity in getattr(response, "deleted_bodies", [])
],
}

def __read_existing_design(self) -> None:
"""Read an existing ``Design`` located on the server."""
#
Expand Down Expand Up @@ -1343,7 +1392,11 @@ def _handle_deleted_bodies(self, deleted_bodies):
for body in self.bodies:
if body.id == body_id:
body._is_alive = False
self.bodies.remove(body)
for bd in self._master_component.part.bodies:
if bd.id == body_id:
self._master_component.part.bodies.remove(bd)
break
self._clear_cached_bodies()
removed = True
self._grpc_client.log.info(
f"Deleted body (ID: {body_id}) removed from root level."
Expand All @@ -1370,10 +1423,11 @@ def _handle_created_bodies(self, created_bodies):
)
continue

added = any(self._find_and_add_body(body_info, self.components))
added = self._find_and_add_body(body_info, self.components)
if not added:
new_body = MasterBody(body_id, body_name, self._grpc_client, is_surface=is_surface)
self.bodies.append(new_body)
self._master_component.part.bodies.append(new_body)
self._clear_cached_bodies()
self._grpc_client.log.debug(
f"Added new body '{body_name}' (ID: {body_id}) to root level."
)
Expand All @@ -1388,14 +1442,17 @@ def _update_body(self, existing_body, body_info):

def _find_and_add_body(self, body_info, components):
for component in components:
if component.id == body_info["parent_id"]:
parent_id_for_body = component._master_component.part.id
if parent_id_for_body == body_info["parent_id"]:
new_body = MasterBody(
body_info["id"],
body_info["name"],
self._grpc_client,
is_surface=body_info.get("is_surface", False),
)
component.bodies.append(new_body)
# component.bodies.append(new_body)
component._master_component.part.bodies.append(new_body)
component._clear_cached_bodies()
self._grpc_client.log.debug(
f"Added new body '{new_body.name}' (ID: {new_body.id}) "
f"to component '{component.name}' (ID: {component.id})"
Expand Down Expand Up @@ -1425,11 +1482,17 @@ def _find_and_update_body(self, body_info, component):

def _find_and_remove_body(self, body_info, component):
for body in component.bodies:
if body.id == body_info["id"]:
body_info_id = body_info["id"]
if body.id == f"{component.id}/{body_info_id}":
body._is_alive = False
component.bodies.remove(body)
# component.bodies.remove(body)
for bd in component._master_component.part.bodies:
if bd.id == body_info_id:
component._master_component.part.bodies.remove(bd)
break
component._clear_cached_bodies()
self._grpc_client.log.debug(
f"Removed body '{body_info['name']}' (ID: {body_info['id']}) from component "
f"Removed body (ID: {body_info['id']}) from component "
f"'{component.name}' (ID: {component.id})"
)
return True
Expand Down
Loading
Loading