Skip to content

Commit 8a5567c

Browse files
smereupyansys-ci-botRobPasMuepre-commit-ci[bot]
authored
feat: Logo detection (#1873)
Co-authored-by: PyAnsys CI Bot <[email protected]> Co-authored-by: Roberto Pastor Muela <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent c202b02 commit 8a5567c

File tree

5 files changed

+196
-1
lines changed

5 files changed

+196
-1
lines changed

doc/changelog.d/1873.added.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Logo detection

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

+104-1
Original file line numberDiff line numberDiff line change
@@ -23,25 +23,30 @@
2323

2424
from typing import TYPE_CHECKING
2525

26+
from beartype import beartype as check_input_types
2627
from google.protobuf.wrappers_pb2 import BoolValue, DoubleValue
2728

2829
from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier
29-
from ansys.api.geometry.v0.models_pb2 import Body as GRPCBody, Face as GRPCFace
30+
from ansys.api.geometry.v0.models_pb2 import Body as GRPCBody, Face as GRPCFace, FindLogoOptions
3031
from ansys.api.geometry.v0.preparetools_pb2 import (
3132
ExtractVolumeFromEdgeLoopsRequest,
3233
ExtractVolumeFromFacesRequest,
34+
FindLogosRequest,
3335
RemoveRoundsRequest,
3436
ShareTopologyRequest,
3537
)
3638
from ansys.api.geometry.v0.preparetools_pb2_grpc import PrepareToolsStub
3739
from ansys.geometry.core.connection import GrpcClient
40+
from ansys.geometry.core.connection.backend import BackendType
3841
from ansys.geometry.core.errors import protect_grpc
42+
from ansys.geometry.core.logger import LOG
3943
from ansys.geometry.core.misc.auxiliary import (
4044
get_bodies_from_ids,
4145
get_design_from_edge,
4246
get_design_from_face,
4347
)
4448
from ansys.geometry.core.misc.checks import check_type_all_elements_in_iterable, min_backend_version
49+
from ansys.geometry.core.tools.problem_areas import LogoProblemArea
4550
from ansys.geometry.core.tools.repair_tool_message import RepairToolMessage
4651
from ansys.geometry.core.typing import Real
4752

@@ -295,3 +300,101 @@ def enhanced_share_topology(
295300
share_topo_response.repaired,
296301
)
297302
return message
303+
304+
@check_input_types
305+
@protect_grpc
306+
@min_backend_version(25, 2, 0)
307+
def find_logos(
308+
self, bodies: list["Body"] = None, min_height: Real = None, max_height: Real = None
309+
) -> "LogoProblemArea":
310+
"""Detect logos in geometry.
311+
312+
Detects logos, using a list of bodies if provided.
313+
The logos are returned as a list of faces.
314+
315+
Parameters
316+
----------
317+
bodies : list[Body], optional
318+
List of bodies where logos should be detected
319+
min_height : real, optional
320+
The minimum height when searching for logos
321+
max_height: real, optional
322+
The minimum height when searching for logos
323+
324+
Returns
325+
-------
326+
LogoProblemArea
327+
Problem area with logo faces.
328+
"""
329+
from ansys.geometry.core.designer.body import Body
330+
331+
if BackendType.is_linux_service(self._grpc_client.backend_type):
332+
# not yet available in Linux
333+
LOG.warning("Logo detection not available on Linux")
334+
return
335+
336+
# Verify inputs
337+
if bodies and len(bodies) > 0:
338+
check_type_all_elements_in_iterable(bodies, Body)
339+
340+
body_ids = [] if bodies is None else [body._grpc_id for body in bodies]
341+
find_logo_options = FindLogoOptions(
342+
min_height=min_height,
343+
max_height=max_height,
344+
)
345+
346+
response = self._prepare_stub.FindLogos(
347+
FindLogosRequest(bodies=body_ids, options=find_logo_options)
348+
)
349+
350+
return LogoProblemArea(
351+
id=response.id,
352+
grpc_client=self._grpc_client,
353+
face_ids=[grpc_face.id for grpc_face in response.logo_faces],
354+
)
355+
356+
@check_input_types
357+
@protect_grpc
358+
@min_backend_version(25, 2, 0)
359+
def find_and_remove_logos(
360+
self, bodies: list["Body"] = None, min_height: Real = None, max_height: Real = None
361+
) -> bool:
362+
"""Detect and remove logos in geometry.
363+
364+
Detects and remove logos, using a list of bodies if provided.
365+
366+
Parameters
367+
----------
368+
bodies : list[Body], optional
369+
List of bodies where logos should be detected and removed.
370+
min_height : real, optional
371+
The minimum height when searching for logos
372+
max_height: real, optional
373+
The minimum height when searching for logos
374+
375+
Returns
376+
-------
377+
Boolean value indicating whether the operation was successful.
378+
"""
379+
from ansys.geometry.core.designer.body import Body
380+
381+
if BackendType.is_linux_service(self._grpc_client.backend_type):
382+
# not yet available in Linux
383+
LOG.warning("Logo detection not available on Linux")
384+
return
385+
386+
# Verify inputs
387+
if bodies and len(bodies) > 0:
388+
check_type_all_elements_in_iterable(bodies, Body)
389+
390+
body_ids = [] if bodies is None else [body._grpc_id for body in bodies]
391+
find_logo_options = FindLogoOptions(
392+
min_height=min_height,
393+
max_height=max_height,
394+
)
395+
396+
response = self._prepare_stub.FindAndRemoveLogos(
397+
FindLogosRequest(bodies=body_ids, options=find_logo_options)
398+
)
399+
400+
return response.success

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

+48
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@
2626

2727
from google.protobuf.wrappers_pb2 import Int32Value
2828

29+
from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier
30+
from ansys.api.geometry.v0.preparetools_pb2 import (
31+
RemoveLogoRequest,
32+
)
33+
from ansys.api.geometry.v0.preparetools_pb2_grpc import PrepareToolsStub
2934
from ansys.api.geometry.v0.repairtools_pb2 import (
3035
FixAdjustSimplifyRequest,
3136
FixDuplicateFacesRequest,
@@ -71,6 +76,7 @@ def __init__(self, id: str, grpc_client: GrpcClient):
7176
self._id = id
7277
self._grpc_id = Int32Value(value=int(id))
7378
self._repair_stub = RepairToolsStub(grpc_client.channel)
79+
self._prepare_stub = PrepareToolsStub(grpc_client.channel)
7480

7581
@property
7682
def id(self) -> str:
@@ -619,3 +625,45 @@ def fix(self) -> RepairToolMessage:
619625
message = RepairToolMessage(response.result.success, [], [])
620626

621627
return message
628+
629+
630+
class LogoProblemArea(ProblemArea):
631+
"""Represents a logo problem area defined by a list of faces.
632+
633+
Parameters
634+
----------
635+
id : str
636+
Server-defined ID for the problem area.
637+
grpc_client : GrpcClient
638+
Active supporting geometry service instance for design modeling.
639+
faces : list[str]
640+
List of faces defining the logo problem area.
641+
"""
642+
643+
def __init__(self, id: str, grpc_client: GrpcClient, face_ids: list[str]):
644+
"""Initialize a new instance of the logo problem area class."""
645+
super().__init__(id, grpc_client)
646+
647+
self._face_ids = face_ids
648+
649+
@property
650+
def face_ids(self) -> list[str]:
651+
"""The ids of the faces defining the logos."""
652+
return self._face_ids
653+
654+
@protect_grpc
655+
def fix(self) -> bool:
656+
"""Fix the problem area by deleting the logos.
657+
658+
Returns
659+
-------
660+
message: bool
661+
Message that return whether the operation was successful.
662+
"""
663+
if len(self._face_ids) == 0:
664+
return False
665+
666+
entity_ids = [EntityIdentifier(id=face_id) for face_id in self._face_ids]
667+
response = self._prepare_stub.RemoveLogo(RemoveLogoRequest(face_ids=entity_ids))
668+
669+
return response.success
1.54 MB
Binary file not shown.

tests/integration/test_prepare_tools.py

+43
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
from pint import Quantity
2525

26+
from ansys.geometry.core.connection.backend import BackendType
2627
from ansys.geometry.core.math.point import Point2D
2728
from ansys.geometry.core.misc.measurements import UNITS
2829
from ansys.geometry.core.modeler import Modeler
@@ -112,3 +113,45 @@ def test_enhanced_share_topology(modeler: Modeler):
112113
result = modeler.prepare_tools.enhanced_share_topology(design.bodies, 0.000554167, True)
113114
assert result.found == 14
114115
assert result.repaired == 14
116+
117+
118+
def test_detect_logos(modeler: Modeler):
119+
"""Test logos are detected and deleted."""
120+
if BackendType.is_linux_service(modeler.client.backend_type):
121+
# not yet available in Linux
122+
return
123+
design = modeler.open_file(FILES_DIR / "partWithLogos.scdocx")
124+
component = [c for c in design.components if c.name == "Default"][0]
125+
body = [b for b in component.bodies if b.name == "Solid3"][0]
126+
assert len(body.faces) == 189
127+
result = modeler.prepare_tools.find_logos()
128+
# no logos should be found is max height is not given
129+
assert len(result.face_ids) == 0
130+
result = modeler.prepare_tools.find_logos(max_height=0.005)
131+
assert len(result.face_ids) == 147
132+
success = modeler.prepare_tools.find_and_remove_logos(max_height=0.005)
133+
assert success is True
134+
assert len(body.faces) == 42
135+
136+
137+
def test_detect_and_fix_logo_as_problem_area(modeler: Modeler):
138+
"""Test logos are detected and deleted as problem area"""
139+
if BackendType.is_linux_service(modeler.client.backend_type):
140+
# not yet available in Linux
141+
return
142+
design = modeler.open_file(FILES_DIR / "partWithLogos.scdocx")
143+
# Get the component named "Default"
144+
component = [c for c in design.components if c.name == "Default"][0]
145+
# test that no issue occurs when no logos are found on body named Solid1
146+
bodies = [b for b in component.bodies if b.name == "Solid1"]
147+
result = modeler.prepare_tools.find_logos(bodies, max_height=0.005)
148+
assert len(result.face_ids) == 0
149+
success = result.fix()
150+
assert success is False
151+
# Remove logos from body named Solid3
152+
bodies = [b for b in component.bodies if b.name == "Solid3"]
153+
result = modeler.prepare_tools.find_logos(bodies, max_height=0.005)
154+
assert len(result.face_ids) == 147
155+
result.fix()
156+
assert success is False
157+
assert len(design.components[0].bodies[2].faces) == 42

0 commit comments

Comments
 (0)