Skip to content

Commit 4ab4835

Browse files
RobPasMuepre-commit-ci[bot]pyansys-ci-bot
authored
feat: use service colors in plotter (upon request) (#1376)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: pyansys-ci-bot <[email protected]>
1 parent 6e25667 commit 4ab4835

File tree

13 files changed

+421
-39
lines changed

13 files changed

+421
-39
lines changed

doc/changelog.d/1376.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
use service colors in plotter (upon request)
21.3 KB
Loading

doc/source/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ def intersphinx_pyansys_geometry(switcher_version: str):
279279
"examples/03_modeling/revolving": "_static/thumbnails/revolving.png",
280280
"examples/03_modeling/export_design": "_static/thumbnails/export_design.png",
281281
"examples/03_modeling/design_tree": "_static/thumbnails/design_tree.png",
282+
"examples/03_modeling/service_colors": "_static/thumbnails/service_colors.png",
282283
"examples/04_applied/01_naca_airfoils": "_static/thumbnails/naca_airfoils.png",
283284
"examples/04_applied/02_naca_fluent": "_static/thumbnails/naca_fluent.png",
284285
}

doc/source/examples.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ These examples demonstrate service-based modeling operations.
4646
examples/03_modeling/revolving.mystnb
4747
examples/03_modeling/export_design.mystnb
4848
examples/03_modeling/design_tree.mystnb
49+
examples/03_modeling/service_colors.mystnb
4950

5051
Applied examples
5152
----------------
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
---
2+
jupytext:
3+
text_representation:
4+
extension: .mystnb
5+
format_name: myst
6+
format_version: 0.13
7+
jupytext_version: 1.14.1
8+
kernelspec:
9+
display_name: Python 3 (ipykernel)
10+
language: python
11+
name: python3
12+
---
13+
# Modeling: Body color assignment and usage
14+
15+
In PyAnsys Geometry, a *body* represents solids or surfaces organized within the ``Design`` assembly.
16+
As users might be already familiar with, Ansys CAD products (like SpaceClaim, Ansys Discovery and the
17+
Geometry Service), allow to assign colors to bodies. This example shows how to assign colors to a body,
18+
retrieve their value and how to use them in the client-side visualization.
19+
20+
## Perform required imports
21+
22+
Perform the required imports.
23+
24+
```{code-cell} ipython3
25+
import ansys.geometry.core as pyansys_geometry
26+
27+
from ansys.geometry.core import Modeler
28+
from ansys.geometry.core.math import Point2D, UNITVECTOR3D_X, UNITVECTOR3D_Y
29+
from ansys.geometry.core.sketch import Sketch
30+
```
31+
32+
## Create a box sketch
33+
34+
Create a ``Sketch`` instance and insert a box sketch with a width and height of 10
35+
in the default plane.
36+
37+
```{code-cell} ipython3
38+
sketch = Sketch()
39+
sketch.box(Point2D([0, 0]), 10, 10)
40+
```
41+
42+
## Initiate design on server
43+
44+
Establish a server connection and initiate a design on the server.
45+
46+
```{code-cell} ipython3
47+
modeler = Modeler()
48+
design = modeler.create_design("ServiceColors")
49+
```
50+
51+
## Extrude the box sketch to create the matrix style design
52+
53+
Given our initial sketch, we will extrude it to create a matrix style design.
54+
We will create a 2x3 matrix of bodies. Each body will be separated by 30 units
55+
in the X direction and 30 units in the Y direction. We will have a total of 6 bodies.
56+
57+
```{code-cell} ipython3
58+
translate = [[0, 30, 60], [0, 30, 60]]
59+
60+
for r_idx, row in enumerate(translate):
61+
comp = design.add_component(f"Component{r_idx}")
62+
63+
for b_idx, dist in enumerate(row):
64+
body = comp.extrude_sketch(f"Component{r_idx}_Body{b_idx}", sketch, distance=10)
65+
body.translate(UNITVECTOR3D_Y, r_idx*30)
66+
body.translate(UNITVECTOR3D_X, dist)
67+
68+
design.plot()
69+
```
70+
71+
## Assign colors to the bodies
72+
73+
Given our previous design, we will assign a color to each body. We will assign
74+
a different color to each one of them. We could have done this assignment while
75+
creating the bodies, but we will do it now for the sake of encapsulating the
76+
color assignment logic.
77+
78+
```{code-cell} ipython3
79+
80+
colors = [["red", "blue", "yellow"], ["orange", "green", "purple"]]
81+
82+
for c_idx, comp in enumerate(design.components):
83+
for b_idx, body in enumerate(comp.bodies):
84+
body.color = colors[c_idx][b_idx]
85+
print(f"Body {body.name} has color {body.color}")
86+
```
87+
88+
## Plotting the design with colors
89+
90+
By default, the plot method will **not** use the colors assigned to the bodies.
91+
To plot the design with the assigned colors, we need to specifically request it.
92+
93+
Users have two options for plotting with the assigned colors:
94+
95+
* Pass the parameter ``use_service_colors=True`` to the plot method.
96+
* Set the global parameter ``USE_SERVICE_COLORS`` to ``True``.
97+
98+
It is important to note that the usage of colors when plotting might slow down the
99+
plotting process, as it requires additional information to be sent from the server
100+
to the client and processed in the client side.
101+
102+
If we just request the plot without setting the global parameter, the plot will
103+
be displayed without the colors, as shown below.
104+
105+
```{code-cell} ipython3
106+
design.plot()
107+
```
108+
109+
As stated previously, if we pass the parameter ``use_service_colors=True`` to the plot
110+
method, the plot will be displayed with the assigned colors.
111+
112+
```{code-cell} ipython3
113+
design.plot(use_service_colors=True)
114+
```
115+
116+
However, if we set the global parameter to ``True``, the plot will be displayed
117+
with the assigned colors without the need to pass the parameter to the plot method.
118+
119+
```{code-cell} ipython3
120+
import ansys.geometry.core as pyansys_geometry
121+
122+
pyansys_geometry.USE_SERVICE_COLORS = True
123+
124+
design.plot()
125+
126+
# Reverting the global parameter to its default value
127+
pyansys_geometry.USE_SERVICE_COLORS = False
128+
```
129+
130+
This last method is useful when the user wants to plot all the designs with the
131+
assigned colors without the need to pass the parameter to the plot method in
132+
every call.
133+
134+
135+
## Plotting specific bodies or components with colors
136+
137+
If the user wants to plot specific bodies with the assigned colors, the user can
138+
follow the same approach as before. The user can pass the parameter ``use_service_colors=True``
139+
to the plot method or set the global parameter ``USE_SERVICE_COLORS`` to ``True``.
140+
141+
In the following examples, we will just demonstrate how to do this using the
142+
``use_service_colors=True`` parameter.
143+
144+
Let's plot the first body of the first component with the assigned colors.
145+
146+
```{code-cell} ipython3
147+
body = design.components[0].bodies[0]
148+
149+
body.plot(use_service_colors=True)
150+
```
151+
152+
Now, let's plot the second component with the assigned colors.
153+
154+
```{code-cell} ipython3
155+
comp = design.components[1]
156+
157+
comp.plot(use_service_colors=True)
158+
```

src/ansys/geometry/core/__init__.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,9 @@
5050

5151
# Global config constants
5252
# ------------------------------------------------------------------------------
53-
54-
USE_TRAME: bool = False
55-
"""Global constant for checking whether to use `trame <https://kitware.github.io/trame/>`_
56-
for visualization."""
53+
USE_SERVICE_COLORS: bool = False
54+
"""Global constant for checking whether to use service colors for plotting
55+
purposes. If set to False, the default colors will be used (speed gain)."""
5756

5857
DISABLE_MULTIPLE_DESIGN_CHECK: bool = False
5958
"""Global constant for disabling the ``ensure_design_is_active`` check.

src/ansys/geometry/core/designer/body.py

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
ProjectCurvesRequest,
5555
)
5656
from ansys.api.geometry.v0.commands_pb2_grpc import CommandsStub
57+
from ansys.geometry.core.connection.backend import BackendType
5758
from ansys.geometry.core.connection.client import GrpcClient
5859
from ansys.geometry.core.connection.conversions import (
5960
frame_to_grpc_frame,
@@ -81,6 +82,7 @@
8182
from ansys.geometry.core.misc.measurements import DEFAULT_UNITS, Angle, Distance
8283
from ansys.geometry.core.sketch.sketch import Sketch
8384
from ansys.geometry.core.typing import Real
85+
from ansys.tools.visualization_interface.utils.color import Color
8486

8587
if TYPE_CHECKING: # pragma: no cover
8688
from pyvista import MultiBlock, PolyData
@@ -546,6 +548,7 @@ def plot(
546548
merge: bool = False,
547549
screenshot: str | None = None,
548550
use_trame: bool | None = None,
551+
use_service_colors: bool | None = None,
549552
**plotting_options: dict | None,
550553
) -> None:
551554
"""Plot the body.
@@ -560,8 +563,12 @@ def plot(
560563
Path for saving a screenshot of the image that is being represented.
561564
use_trame : bool, default: None
562565
Whether to enable the use of `trame <https://kitware.github.io/trame/index.html>`_.
563-
The default is ``None``, in which case the ``USE_TRAME`` global setting
564-
is used.
566+
The default is ``None``, in which case the
567+
``ansys.tools.visualization_interface.USE_TRAME`` global setting is used.
568+
use_service_colors : bool, default: None
569+
Whether to use the colors assigned to the body in the service. The default
570+
is ``None``, in which case the ``ansys.geometry.core.USE_SERVICE_COLORS``
571+
global setting is used.
565572
**plotting_options : dict, default: None
566573
Keyword arguments for plotting. For allowable keyword arguments, see the
567574
:meth:`Plotter.add_mesh <pyvista.Plotter.add_mesh>` method.
@@ -757,24 +764,27 @@ def fill_style(self, value: FillStyle): # noqa: D102
757764
@property
758765
def color(self) -> str: # noqa: D102
759766
"""Get the current color of the body."""
760-
if self._color is None:
761-
if self._grpc_client.backend_version < (25, 1, 0): # pragma: no cover
767+
if self._color is None and self.is_alive:
768+
# Assigning default value first
769+
self._color = Color.DEFAULT.value
770+
771+
# TODO: Remove this check when the Linux service backend supports color setting
772+
# https://github.com/ansys/pyansys-geometry/issues/1383
773+
if self._grpc_client.backend_type == BackendType.LINUX_SERVICE:
774+
self._grpc_client.log.warning(
775+
"Colors are not supported in the Linux backend. Default value assigned..."
776+
)
777+
elif self._grpc_client.backend_version < (25, 1, 0): # pragma: no cover
762778
# Server does not support color retrieval before version 25.1.0
763779
self._grpc_client.log.warning(
764-
"Server does not support color retrieval. Assigning default."
780+
"Server does not support color retrieval. Default value assigned..."
765781
)
766-
self._color = "#000000" # Default color
767782
else:
768783
# Fetch color from the server if it's not cached
769784
color_response = self._bodies_stub.GetColor(EntityIdentifier(id=self._id))
770-
771785
if color_response.color:
772786
self._color = mcolors.to_hex(color_response.color)
773-
else: # pragma: no cover
774-
self._grpc_client.log.warning(
775-
f"Color could not be retrieved for body {self._id}. Assigning default."
776-
)
777-
self._color = "#000000" # Default color
787+
778788
return self._color
779789

780790
@color.setter
@@ -980,6 +990,15 @@ def set_color(self, color: str | tuple[float, float, float]) -> None:
980990
"""Set the color of the body."""
981991
self._grpc_client.log.debug(f"Setting body color of {self.id} to {color}.")
982992

993+
# TODO: Remove this check when the Linux service backend supports color setting
994+
# https://github.com/ansys/pyansys-geometry/issues/1383
995+
if self._grpc_client.backend_type == BackendType.LINUX_SERVICE:
996+
self._grpc_client.log.warning(
997+
"Setting color is not supported in the Linux service backend."
998+
)
999+
self._grpc_client.log.warning("Ignoring request...")
1000+
return
1001+
9831002
try:
9841003
if isinstance(color, tuple):
9851004
# Ensure that all elements are within 0-1 or 0-255 range
@@ -1125,6 +1144,7 @@ def plot( # noqa: D102
11251144
merge: bool = False,
11261145
screenshot: str | None = None,
11271146
use_trame: bool | None = None,
1147+
use_service_colors: bool | None = None,
11281148
**plotting_options: dict | None,
11291149
) -> None:
11301150
raise NotImplementedError(
@@ -1509,17 +1529,27 @@ def plot( # noqa: D102
15091529
merge: bool = False,
15101530
screenshot: str | None = None,
15111531
use_trame: bool | None = None,
1532+
use_service_colors: bool | None = None,
15121533
**plotting_options: dict | None,
15131534
) -> None:
15141535
# lazy import here to improve initial module load time
1536+
import ansys.geometry.core as pyansys_geometry
15151537
from ansys.geometry.core.plotting import GeometryPlotter
15161538
from ansys.tools.visualization_interface.types.mesh_object_plot import (
15171539
MeshObjectPlot,
15181540
)
15191541

1520-
meshobject = MeshObjectPlot(self, self.tessellate(merge=merge))
1521-
pl = GeometryPlotter(use_trame=use_trame)
1522-
pl.plot(meshobject, **plotting_options)
1542+
use_service_colors = (
1543+
use_service_colors
1544+
if use_service_colors is not None
1545+
else pyansys_geometry.USE_SERVICE_COLORS
1546+
)
1547+
1548+
mesh_object = (
1549+
self if use_service_colors else MeshObjectPlot(self, self.tessellate(merge=merge))
1550+
)
1551+
pl = GeometryPlotter(use_trame=use_trame, use_service_colors=use_service_colors)
1552+
pl.plot(mesh_object, **plotting_options)
15231553
pl.show(screenshot=screenshot, **plotting_options)
15241554

15251555
def intersect(self, other: Union["Body", Iterable["Body"]], keep_other: bool = False) -> None: # noqa: D102
@@ -1596,6 +1626,7 @@ def __repr__(self) -> str:
15961626
if self.is_surface:
15971627
lines.append(f" Surface thickness : {self.surface_thickness}")
15981628
lines.append(f" Surface offset : {self.surface_offset}")
1629+
lines.append(f" Color : {self.color}")
15991630

16001631
nl = "\n"
16011632
return f"{nl}{nl.join(lines)}{nl}"

src/ansys/geometry/core/designer/component.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1388,6 +1388,7 @@ def plot(
13881388
merge_bodies: bool = False,
13891389
screenshot: str | None = None,
13901390
use_trame: bool | None = None,
1391+
use_service_colors: bool | None = None,
13911392
**plotting_options: dict | None,
13921393
) -> None:
13931394
"""Plot the component.
@@ -1406,8 +1407,12 @@ def plot(
14061407
Path for saving a screenshot of the image being represented.
14071408
use_trame : bool, default: None
14081409
Whether to enable the use of `trame <https://kitware.github.io/trame/index.html>`_.
1409-
The default is ``None``, in which case the ``USE_TRAME`` global setting
1410-
is used.
1410+
The default is ``None``, in which case the
1411+
``ansys.tools.visualization_interface.USE_TRAME`` global setting is used.
1412+
use_service_colors : bool, default: None
1413+
Whether to use the colors assigned to the body in the service. The default
1414+
is ``None``, in which case the ``ansys.geometry.core.USE_SERVICE_COLORS``
1415+
global setting is used.
14111416
**plotting_options : dict, default: None
14121417
Keyword arguments for plotting. For allowable keyword arguments, see the
14131418
@@ -1446,13 +1451,25 @@ def plot(
14461451
N Coordinate Systems : 0
14471452
>>> mycomp.plot(pbr=True, metallic=1.0)
14481453
"""
1454+
import ansys.geometry.core as pyansys_geometry
14491455
from ansys.geometry.core.plotting import GeometryPlotter
14501456
from ansys.tools.visualization_interface.types.mesh_object_plot import MeshObjectPlot
14511457

1452-
mesh_object = MeshObjectPlot(
1453-
custom_object=self, mesh=self.tessellate(merge_component, merge_bodies)
1458+
use_service_colors = (
1459+
use_service_colors
1460+
if use_service_colors is not None
1461+
else pyansys_geometry.USE_SERVICE_COLORS
14541462
)
1455-
pl = GeometryPlotter(use_trame=use_trame)
1463+
1464+
mesh_object = (
1465+
self
1466+
if use_service_colors
1467+
else MeshObjectPlot(
1468+
custom_object=self,
1469+
mesh=self.tessellate(merge_component=merge_component, merge_bodies=merge_bodies),
1470+
)
1471+
)
1472+
pl = GeometryPlotter(use_trame=use_trame, use_service_colors=use_service_colors)
14561473
pl.plot(mesh_object, **plotting_options)
14571474
pl.show(screenshot=screenshot, **plotting_options)
14581475

0 commit comments

Comments
 (0)