|
28 | 28 | from typing import TYPE_CHECKING, Union
|
29 | 29 |
|
30 | 30 | from beartype import beartype as check_input_types
|
| 31 | +import matplotlib.colors as mcolors |
31 | 32 | from pint import Quantity
|
32 | 33 |
|
33 | 34 | from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier
|
|
40 | 41 | RotateRequest,
|
41 | 42 | ScaleRequest,
|
42 | 43 | SetAssignedMaterialRequest,
|
| 44 | + SetColorRequest, |
43 | 45 | SetFillStyleRequest,
|
44 | 46 | SetNameRequest,
|
45 | 47 | TranslateRequest,
|
@@ -149,6 +151,23 @@ def set_fill_style(self, fill_style: FillStyle) -> None:
|
149 | 151 | """Set the fill style of the body."""
|
150 | 152 | return
|
151 | 153 |
|
| 154 | + @abstractmethod |
| 155 | + def color(self) -> str: |
| 156 | + """Get the color of the body.""" |
| 157 | + return |
| 158 | + |
| 159 | + @abstractmethod |
| 160 | + def set_color(self, color: str | tuple[float, float, float]) -> None: |
| 161 | + """Set the color of the body. |
| 162 | +
|
| 163 | + Parameters |
| 164 | + ---------- |
| 165 | + color : str | tuple[float, float, float] |
| 166 | + Color to set the body to. This can be a string representing a color name |
| 167 | + or a tuple of RGB values in the range [0, 1] (RGBA) or [0, 255] (pure RGB). |
| 168 | + """ |
| 169 | + return |
| 170 | + |
152 | 171 | @abstractmethod
|
153 | 172 | def faces(self) -> list[Face]:
|
154 | 173 | """Get a list of all faces within the body.
|
@@ -687,6 +706,7 @@ def __init__(
|
687 | 706 | self._commands_stub = CommandsStub(self._grpc_client.channel)
|
688 | 707 | self._tessellation = None
|
689 | 708 | self._fill_style = FillStyle.DEFAULT
|
| 709 | + self._color = None |
690 | 710 |
|
691 | 711 | def reset_tessellation_cache(func): # noqa: N805
|
692 | 712 | """Decorate ``MasterBody`` methods that need tessellation cache update.
|
@@ -734,6 +754,33 @@ def fill_style(self) -> str: # noqa: D102
|
734 | 754 | def fill_style(self, value: FillStyle): # noqa: D102
|
735 | 755 | self.set_fill_style(value)
|
736 | 756 |
|
| 757 | + @property |
| 758 | + def color(self) -> str: # noqa: D102 |
| 759 | + """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 |
| 762 | + # Server does not support color retrieval before version 25.1.0 |
| 763 | + self._grpc_client.log.warning( |
| 764 | + "Server does not support color retrieval. Assigning default." |
| 765 | + ) |
| 766 | + self._color = "#000000" # Default color |
| 767 | + else: |
| 768 | + # Fetch color from the server if it's not cached |
| 769 | + color_response = self._bodies_stub.GetColor(EntityIdentifier(id=self._id)) |
| 770 | + |
| 771 | + if color_response.color: |
| 772 | + 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 |
| 778 | + return self._color |
| 779 | + |
| 780 | + @color.setter |
| 781 | + def color(self, value: str | tuple[float, float, float]): # noqa: D102 |
| 782 | + self.set_color(value) |
| 783 | + |
737 | 784 | @property
|
738 | 785 | def is_surface(self) -> bool: # noqa: D102
|
739 | 786 | return self._is_surface
|
@@ -926,6 +973,43 @@ def set_fill_style( # noqa: D102
|
926 | 973 | )
|
927 | 974 | self._fill_style = fill_style
|
928 | 975 |
|
| 976 | + @protect_grpc |
| 977 | + @check_input_types |
| 978 | + @min_backend_version(25, 1, 0) |
| 979 | + def set_color(self, color: str | tuple[float, float, float]) -> None: |
| 980 | + """Set the color of the body.""" |
| 981 | + self._grpc_client.log.debug(f"Setting body color of {self.id} to {color}.") |
| 982 | + |
| 983 | + try: |
| 984 | + if isinstance(color, tuple): |
| 985 | + # Ensure that all elements are within 0-1 or 0-255 range |
| 986 | + if all(0 <= c <= 1 for c in color): |
| 987 | + # Ensure they are floats if in 0-1 range |
| 988 | + if not all(isinstance(c, float) for c in color): |
| 989 | + raise ValueError("RGB values in the 0-1 range must be floats.") |
| 990 | + elif all(0 <= c <= 255 for c in color): |
| 991 | + # Ensure they are integers if in 0-255 range |
| 992 | + if not all(isinstance(c, int) for c in color): |
| 993 | + raise ValueError("RGB values in the 0-255 range must be integers.") |
| 994 | + # Normalize the 0-255 range to 0-1 |
| 995 | + color = tuple(c / 255.0 for c in color) |
| 996 | + else: |
| 997 | + raise ValueError("RGB tuple contains mixed ranges or invalid values.") |
| 998 | + |
| 999 | + color = mcolors.to_hex(color) |
| 1000 | + elif isinstance(color, str): |
| 1001 | + color = mcolors.to_hex(color) |
| 1002 | + except ValueError as err: |
| 1003 | + raise ValueError(f"Invalid color value: {err}") |
| 1004 | + |
| 1005 | + self._bodies_stub.SetColor( |
| 1006 | + SetColorRequest( |
| 1007 | + body_id=self.id, |
| 1008 | + color=color, |
| 1009 | + ) |
| 1010 | + ) |
| 1011 | + self._color = color |
| 1012 | + |
929 | 1013 | @protect_grpc
|
930 | 1014 | @check_input_types
|
931 | 1015 | @reset_tessellation_cache
|
@@ -1140,6 +1224,14 @@ def fill_style(self) -> str: # noqa: D102
|
1140 | 1224 | def fill_style(self, fill_style: FillStyle) -> str: # noqa: D102
|
1141 | 1225 | self._template.fill_style = fill_style
|
1142 | 1226 |
|
| 1227 | + @property |
| 1228 | + def color(self) -> str: # noqa: D102 |
| 1229 | + return self._template.color |
| 1230 | + |
| 1231 | + @color.setter |
| 1232 | + def color(self, color: str | tuple[float, float, float]) -> None: # noqa: D102 |
| 1233 | + return self._template.set_color(color) |
| 1234 | + |
1143 | 1235 | @property
|
1144 | 1236 | def parent_component(self) -> "Component": # noqa: D102
|
1145 | 1237 | return self._parent_component
|
@@ -1367,6 +1459,10 @@ def set_name(self, name: str) -> None: # noqa: D102
|
1367 | 1459 | def set_fill_style(self, fill_style: FillStyle) -> None: # noqa: D102
|
1368 | 1460 | return self._template.set_fill_style(fill_style)
|
1369 | 1461 |
|
| 1462 | + @ensure_design_is_active |
| 1463 | + def set_color(self, color: str | tuple[float, float, float]) -> None: # noqa: D102 |
| 1464 | + return self._template.set_color(color) |
| 1465 | + |
1370 | 1466 | @ensure_design_is_active
|
1371 | 1467 | def translate( # noqa: D102
|
1372 | 1468 | self, direction: UnitVector3D, distance: Quantity | Distance | Real
|
|
0 commit comments