Skip to content

Commit 7034aaf

Browse files
wip refactoring
1 parent 6960d45 commit 7034aaf

File tree

5 files changed

+163
-22
lines changed

5 files changed

+163
-22
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,11 @@ def find_and_fix_simplify(self, **kwargs) -> dict:
113113
"""Identify and simplify areas in the geometry."""
114114
pass # pragma: no cover
115115

116+
@abstractmethod
117+
def find_and_fix_stitch_faces(self, **kwargs) -> dict:
118+
"""Identify and stitch faces in the geometry."""
119+
pass # pragma: no cover
120+
116121
@abstractmethod
117122
def inspect_geometry(self, **kwargs) -> dict:
118123
"""Inspect the geometry for issues."""

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

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
geometry issues, such as split edges, extra edges, duplicate faces etc.
2727
"""
2828

29+
from google.protobuf.wrappers_pb2 import BoolValue, DoubleValue
2930
import grpc
3031

3132
from ansys.geometry.core.errors import protect_grpc
@@ -170,7 +171,12 @@ def find_duplicate_faces(self, **kwargs) -> dict: # noqa: D102
170171
def find_missing_faces(self, **kwargs) -> dict: # noqa: D102
171172
from ansys.api.geometry.v0.repairtools_pb2 import FindMissingFacesRequest
172173

173-
request = FindMissingFacesRequest(faces=kwargs["faces"])
174+
request = FindMissingFacesRequest(
175+
faces=kwargs["faces"],
176+
angle=DoubleValue(value=kwargs["angle"]),
177+
distance=DoubleValue(value=kwargs["distance"]),
178+
)
179+
174180
# Call the gRPC service
175181
response = self.stub.FindMissingFaces(request)
176182

@@ -208,9 +214,14 @@ def find_small_faces(self, **kwargs) -> dict: # noqa: D102
208214
def find_stitch_faces(self, **kwargs) -> dict: # noqa: D102
209215
from ansys.api.geometry.v0.repairtools_pb2 import FindStitchFacesRequest
210216

211-
request = FindStitchFacesRequest(faces=kwargs["faces"])
217+
request = FindStitchFacesRequest(
218+
faces=kwargs["faces"],
219+
maximum_distance=DoubleValue(value=kwargs["distance"])
220+
)
221+
212222
# Call the gRPC service
213223
response = self.stub.FindStitchFaces(request)
224+
214225
# Return the response - formatted as a dictionary
215226
return {
216227
"problems": [
@@ -260,6 +271,32 @@ def find_and_fix_simplify(self, **kwargs) -> dict: # noqa: D102
260271
"modified_bodies_monikers": [],
261272
}
262273

274+
@protect_grpc
275+
def find_and_fix_stitch_faces(self, **kwargs) -> dict: # noqa: D102
276+
from ansys.api.geometry.v0.repairtools_pb2 import FindStitchFacesRequest
277+
278+
# Create the gRPC request
279+
request = FindStitchFacesRequest(
280+
faces=kwargs["body_ids"],
281+
maximum_distance=DoubleValue(value=kwargs["max_distance"]),
282+
allow_multiple_bodies=BoolValue(value=kwargs["allow_multiple_bodies"]),
283+
maintain_components=BoolValue(value=kwargs["maintain_components"]),
284+
check_for_coincidence=BoolValue(value=kwargs["check_for_coincidence"]),
285+
comprehensive=kwargs["comprehensive_result"],
286+
)
287+
288+
# Call the gRPC service
289+
response = self.stub.FindAndFixStitchFaces(request)
290+
291+
# Return the response - formatted as a dictionary
292+
return {
293+
"success": response.success,
294+
"created_bodies_monikers": response.created_bodies_monikers,
295+
"modified_bodies_monikers": response.modified_bodies_monikers,
296+
"found": response.found,
297+
"repaired": response.repaired,
298+
}
299+
263300
@protect_grpc
264301
def inspect_geometry(self, **kwargs) -> dict: # noqa: D102
265302
from ansys.api.geometry.v0.repairtools_pb2 import InspectGeometryRequest

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ def find_and_fix_split_edges(self, **kwargs) -> dict: # noqa: D102
8080
def find_and_fix_simplify(self, **kwargs) -> dict: # noqa: D102
8181
raise NotImplementedError
8282

83+
def find_and_fix_stitch_faces(self, **kwargs) -> dict: # noqa: D102
84+
raise NotImplementedError
85+
8386
def inspect_geometry(self, **kwargs) -> dict: # noqa: D102
8487
raise NotImplementedError
8588

src/ansys/geometry/core/tools/repair_tools.py

Lines changed: 90 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
from typing import TYPE_CHECKING
2525

2626
from ansys.geometry.core.connection import GrpcClient
27-
from ansys.geometry.core.errors import protect_grpc
2827
from ansys.geometry.core.misc.auxiliary import (
2928
get_bodies_from_ids,
3029
get_design_from_body,
@@ -36,6 +35,7 @@
3635
check_type_all_elements_in_iterable,
3736
min_backend_version,
3837
)
38+
from ansys.geometry.core.misc.measurements import Angle, Distance
3939
from ansys.geometry.core.tools.check_geometry import GeometryIssue, InspectResult
4040
from ansys.geometry.core.tools.problem_areas import (
4141
DuplicateFaceProblemAreas,
@@ -65,7 +65,6 @@ def __init__(self, grpc_client: GrpcClient, modeler: "Modeler"):
6565
self._modeler = modeler
6666
self._grpc_client = grpc_client
6767

68-
@protect_grpc
6968
def find_split_edges(
7069
self, bodies: list["Body"], angle: Real = 0.0, length: Real = 0.0
7170
) -> list[SplitEdgeProblemAreas]:
@@ -107,7 +106,6 @@ def find_split_edges(
107106
for res in problem_areas_response["problems"]
108107
]
109108

110-
@protect_grpc
111109
def find_extra_edges(self, bodies: list["Body"]) -> list[ExtraEdgeProblemAreas]:
112110
"""Find the extra edges in the given list of bodies.
113111
@@ -142,7 +140,6 @@ def find_extra_edges(self, bodies: list["Body"]) -> list[ExtraEdgeProblemAreas]:
142140
for res in problem_areas_response["problems"]
143141
]
144142

145-
@protect_grpc
146143
def find_inexact_edges(self, bodies: list["Body"]) -> list[InexactEdgeProblemAreas]:
147144
"""Find inexact edges in the given list of bodies.
148145
@@ -178,7 +175,6 @@ def find_inexact_edges(self, bodies: list["Body"]) -> list[InexactEdgeProblemAre
178175
for res in problem_areas_response["problems"]
179176
]
180177

181-
@protect_grpc
182178
def find_short_edges(
183179
self, bodies: list["Body"], length: Real = 0.0
184180
) -> list[ShortEdgeProblemAreas]:
@@ -216,7 +212,6 @@ def find_short_edges(
216212
for res in problem_areas_response["problems"]
217213
]
218214

219-
@protect_grpc
220215
def find_duplicate_faces(self, bodies: list["Body"]) -> list[DuplicateFaceProblemAreas]:
221216
"""Find the duplicate face problem areas.
222217
@@ -251,8 +246,12 @@ def find_duplicate_faces(self, bodies: list["Body"]) -> list[DuplicateFaceProble
251246
for res in problem_areas_response["problems"]
252247
]
253248

254-
@protect_grpc
255-
def find_missing_faces(self, bodies: list["Body"]) -> list[MissingFaceProblemAreas]:
249+
def find_missing_faces(
250+
self,
251+
bodies: list["Body"],
252+
angle: Angle = None,
253+
distance: Distance = None,
254+
)-> list[MissingFaceProblemAreas]:
256255
"""Find the missing faces.
257256
258257
This method find the missing face problem areas and returns a list of missing
@@ -262,6 +261,10 @@ def find_missing_faces(self, bodies: list["Body"]) -> list[MissingFaceProblemAre
262261
----------
263262
bodies : list[Body]
264263
List of bodies that missing faces are investigated on.
264+
angle : Angle, optional
265+
The minimum angle between faces. By default, None.
266+
distance : Distance, optional
267+
The minimum distance between faces. By default, None.
265268
266269
Returns
267270
-------
@@ -272,7 +275,9 @@ def find_missing_faces(self, bodies: list["Body"]) -> list[MissingFaceProblemAre
272275
return []
273276
body_ids = [body.id for body in bodies]
274277
problem_areas_response = self._grpc_client.services.repair_tools.find_missing_faces(
275-
faces=body_ids
278+
faces=body_ids,
279+
angle=angle.value if angle is not None else None,
280+
distance=distance.value if distance is not None else None,
276281
)
277282
parent_design = get_design_from_body(bodies[0])
278283

@@ -285,7 +290,6 @@ def find_missing_faces(self, bodies: list["Body"]) -> list[MissingFaceProblemAre
285290
for res in problem_areas_response["problems"]
286291
]
287292

288-
@protect_grpc
289293
def find_small_faces(self, bodies: list["Body"]) -> list[SmallFaceProblemAreas]:
290294
"""Find the small face problem areas.
291295
@@ -320,8 +324,11 @@ def find_small_faces(self, bodies: list["Body"]) -> list[SmallFaceProblemAreas]:
320324
for res in problem_areas_response["problems"]
321325
]
322326

323-
@protect_grpc
324-
def find_stitch_faces(self, bodies: list["Body"]) -> list[StitchFaceProblemAreas]:
327+
def find_stitch_faces(
328+
self,
329+
bodies: list["Body"],
330+
max_distance: Distance = None,
331+
) -> list[StitchFaceProblemAreas]:
325332
"""Return the list of stitch face problem areas.
326333
327334
This method find the stitch face problem areas and returns a list of ids of stitch face
@@ -331,6 +338,8 @@ def find_stitch_faces(self, bodies: list["Body"]) -> list[StitchFaceProblemAreas
331338
----------
332339
bodies : list[Body]
333340
List of bodies that stitchable faces are investigated on.
341+
max_distance : Distance, optional
342+
Maximum distance between faces. By default, None.
334343
335344
Returns
336345
-------
@@ -339,7 +348,8 @@ def find_stitch_faces(self, bodies: list["Body"]) -> list[StitchFaceProblemAreas
339348
"""
340349
body_ids = [body.id for body in bodies]
341350
problem_areas_response = self._grpc_client.services.repair_tools.find_stitch_faces(
342-
faces=body_ids
351+
faces=body_ids,
352+
distance=max_distance.value if max_distance is not None else None,
343353
)
344354
parent_design = get_design_from_body(bodies[0])
345355
return [
@@ -351,7 +361,6 @@ def find_stitch_faces(self, bodies: list["Body"]) -> list[StitchFaceProblemAreas
351361
for res in problem_areas_response["problems"]
352362
]
353363

354-
@protect_grpc
355364
@min_backend_version(25, 2, 0)
356365
def find_simplify(self, bodies: list["Body"]) -> list[UnsimplifiedFaceProblemAreas]:
357366
"""Detect faces in a body that can be simplified.
@@ -385,7 +394,6 @@ def find_simplify(self, bodies: list["Body"]) -> list[UnsimplifiedFaceProblemAre
385394
for res in problem_areas_response["problems"]
386395
]
387396

388-
@protect_grpc
389397
@min_backend_version(25, 2, 0)
390398
def find_interferences(
391399
self, bodies: list["Body"], cut_smaller_body: bool = False
@@ -435,7 +443,6 @@ def find_interferences(
435443
for res in problem_areas_response["problems"]
436444
]
437445

438-
@protect_grpc
439446
@min_backend_version(25, 2, 0)
440447
def find_and_fix_short_edges(
441448
self, bodies: list["Body"], length: Real = 0.0, comprehensive_result: bool = False
@@ -489,7 +496,6 @@ def find_and_fix_short_edges(
489496
)
490497
return message
491498

492-
@protect_grpc
493499
@min_backend_version(25, 2, 0)
494500
def find_and_fix_extra_edges(
495501
self, bodies: list["Body"], comprehensive_result: bool = False
@@ -540,7 +546,6 @@ def find_and_fix_extra_edges(
540546
)
541547
return message
542548

543-
@protect_grpc
544549
@min_backend_version(25, 2, 0)
545550
def find_and_fix_split_edges(
546551
self,
@@ -602,7 +607,6 @@ def find_and_fix_split_edges(
602607
)
603608
return message
604609

605-
@protect_grpc
606610
@min_backend_version(25, 2, 0)
607611
def find_and_fix_simplify(
608612
self, bodies: list["Body"], comprehensive_result: bool = False
@@ -652,6 +656,73 @@ def find_and_fix_simplify(
652656
)
653657
return message
654658

659+
@min_backend_version(25, 2, 0)
660+
def find_and_fix_stitch_faces(
661+
self,
662+
bodies: list["Body"],
663+
max_distance: Distance = None,
664+
allow_multiple_bodies: bool = False,
665+
maintain_components: bool = True,
666+
check_for_coincidence: bool = False,
667+
comprehensive_result: bool = False,
668+
) -> RepairToolMessage:
669+
"""Find and fix the stitch face problem areas.
670+
671+
Parameters
672+
----------
673+
bodies : list[Body]
674+
List of bodies that stitchable faces are investigated on.
675+
max_distance : Real, optional
676+
The maximum distance between faces to be stitched.
677+
By default, 0.0001.
678+
allow_multiple_bodies : bool, optional
679+
Whether to allow multiple bodies in the result.
680+
By default, False.
681+
maintain_components : bool, optional
682+
Whether to stitch bodies within the components.
683+
By default, True.
684+
check_for_coincidence : bool, optional
685+
Whether coincidence surfaces are searched.
686+
By default, False.
687+
comprehensive_result : bool, optional
688+
Whether to fix all problem areas individually.
689+
By default, False.
690+
691+
Returns
692+
-------
693+
RepairToolMessage
694+
Message containing number of problem areas found/fixed, created and/or modified bodies.
695+
696+
Notes
697+
-----
698+
This method finds the stitchable faces and fixes them.
699+
"""
700+
from ansys.geometry.core.designer.body import Body
701+
702+
check_type_all_elements_in_iterable(bodies, Body)
703+
704+
body_ids = [body.id for body in bodies]
705+
706+
response = self._grpc_client.services.repair_tools.find_and_fix_stitch_faces(
707+
body_ids=body_ids,
708+
max_distance=max_distance.value if max_distance is not None else None,
709+
allow_multiple_bodies=allow_multiple_bodies,
710+
maintain_components=maintain_components,
711+
check_for_coincidence=check_for_coincidence,
712+
comprehensive_result=comprehensive_result,
713+
)
714+
715+
parent_design = get_design_from_body(bodies[0])
716+
parent_design._update_design_inplace()
717+
message = RepairToolMessage(
718+
response.get("success"),
719+
response.get("created_bodies_monikers"),
720+
response.get("modified_bodies_monikers"),
721+
response.get("found"),
722+
response.get("repaired"),
723+
)
724+
return message
725+
655726
def inspect_geometry(self, bodies: list["Body"] = None) -> list[InspectResult]:
656727
"""Return a list of geometry issues organized by body.
657728
@@ -715,7 +786,6 @@ def __create_issues_from_response(
715786
issues.append(geometry_issue)
716787
return issues
717788

718-
@protect_grpc
719789
@min_backend_version(25, 2, 0)
720790
def repair_geometry(self, bodies: list["Body"] = None) -> RepairToolMessage:
721791
"""Attempt to repair the geometry for the given bodies.

tests/integration/test_repair_tools.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,32 @@ def test_fix_interference(modeler: Modeler):
285285
assert result.success is True
286286

287287

288+
def test_find_and_fix_stitch_faces(modeler: Modeler):
289+
"""Test to find and fix stitch faces and validate that we get a solid."""
290+
design = modeler.open_file(FILES_DIR / "stitch_1200_bodies.dsco")
291+
assert len(design.bodies) == 3600
292+
293+
stitch_faces = modeler.repair_tools.find_and_fix_stitch_faces(design.bodies)
294+
assert stitch_faces.found == 1
295+
assert stitch_faces.repaired == 1
296+
297+
assert len(design.bodies) == 1200
298+
299+
300+
def test_find_and_fix_stitch_faces_comprehensive(modeler: Modeler):
301+
"""Test to find and fix stitch faces and validate that we get a solid."""
302+
design = modeler.open_file(FILES_DIR / "stitch_1200_bodies.dsco")
303+
assert len(design.bodies) == 3600
304+
305+
stitch_faces = modeler.repair_tools.find_and_fix_stitch_faces(
306+
design.bodies, comprehensive_result=True
307+
)
308+
assert stitch_faces.found == 1200
309+
assert stitch_faces.repaired == 1200
310+
311+
assert len(design.bodies) == 1200
312+
313+
288314
def test_find_and_fix_duplicate_faces(modeler: Modeler):
289315
"""Test to read geometry, find and fix duplicate faces and validate they are removed."""
290316
design = modeler.open_file(FILES_DIR / "DuplicateFaces.scdocx")

0 commit comments

Comments
 (0)