Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Support for Multiple Color Formats in Hallway, Robot, Object and Location class. #307

Merged
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
c37ef5e
Add support for different forms of color input
kumar-sanjeeev Dec 1, 2024
db2cb72
Fix return value of color from tuple to list
kumar-sanjeeev Dec 1, 2024
205fe36
Add test cases to validate differnt color input values
kumar-sanjeeev Dec 1, 2024
b753ba6
Add colors with different form of accepted input types
kumar-sanjeeev Dec 1, 2024
ed721cc
Set bathroom color as original
kumar-sanjeeev Dec 2, 2024
7de16d5
Move parse color in general utlis from room.
kumar-sanjeeev Dec 2, 2024
34c64ee
Fix Typo
kumar-sanjeeev Dec 2, 2024
46c4e4b
Add colors in different input formats
kumar-sanjeeev Dec 2, 2024
c530fe4
Modify green color as original, keeping format as hex
kumar-sanjeeev Dec 3, 2024
31c0641
Add colors in different input formats
kumar-sanjeeev Dec 3, 2024
bea9d03
Fix linting errors
kumar-sanjeeev Dec 3, 2024
d891216
Add unit test for parse color in separate file.
kumar-sanjeeev Dec 3, 2024
7ab0060
Fix type in docstring and update return type of parse_color
kumar-sanjeeev Dec 3, 2024
2c0a964
Add exception catch support
kumar-sanjeeev Dec 7, 2024
07baa2d
Add multicolor formats input to hallway
kumar-sanjeeev Dec 7, 2024
41a6c77
Add multicolor formats input to robots
kumar-sanjeeev Dec 7, 2024
a3894b8
Add support for taking color inputs in multiple formats for objects
kumar-sanjeeev Dec 7, 2024
f40ede7
Add support for taking color inputs in multiple formats to locations
kumar-sanjeeev Dec 7, 2024
b47fcdc
Merge remote-tracking branch 'upstream/main' into feat-support-multip…
kumar-sanjeeev Dec 7, 2024
a2711c5
Modify ValueError description for clarity
kumar-sanjeeev Dec 7, 2024
c6431b2
Fix docs rendering for color parameter
kumar-sanjeeev Dec 7, 2024
f04150d
Update excepted error message
kumar-sanjeeev Dec 7, 2024
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
6 changes: 3 additions & 3 deletions pyrobosim/examples/demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,7 @@ def create_world(multirobot=False):

# Add hallways between the rooms
world.add_hallway(
room_start="kitchen",
room_end="bathroom",
width=0.7,
room_start="kitchen", room_end="bathroom", width=0.7, color="#666666"
)
world.add_hallway(
room_start="bathroom",
Expand All @@ -63,6 +61,7 @@ def create_world(multirobot=False):
conn_method="angle",
conn_angle=0,
offset=0.8,
color="dimgray",
)
world.add_hallway(
room_start="kitchen",
Expand Down Expand Up @@ -122,6 +121,7 @@ def create_world(multirobot=False):
),
grasp_generator=GraspGenerator(grasp_props),
partial_observability=args.partial_observability,
color="#CC00CC",
)
planner_config_rrt = {
"world": world,
Expand Down
10 changes: 7 additions & 3 deletions pyrobosim/pyrobosim/core/hallway.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from ..utils.pose import Pose, get_angle, get_bearing_range
from ..utils.polygon import inflate_polygon
from ..utils.search_graph import Node
from ..utils.general import parse_color


class Hallway:
Expand Down Expand Up @@ -54,8 +55,11 @@ def __init__(
:type conn_angle: float, optional
:param conn_points: If using "points" connection method, specifies the hallway points.
:type conn_points: list[(float, float)], optional
:param color: Visualization color as an (R, G, B) tuple in the range (0.0, 1.0)
:type color: (float, float, float), optional
:param color: Visualization color. Input can be
- an (R, G, B) tuple, list in the range (0.0, 1.0),
- a string (e.g., "red")
- a hexadecimal (e.g., "#FF0000").
:type color: list[float] | tuple[float, float, float] | str
:param wall_width: Width of hallway walls, in meters.
:type wall_width: float, optional
:param is_open: If True, the hallway is open, otherwise it is closed.
Expand All @@ -78,7 +82,7 @@ def __init__(
self.width = width
self.wall_width = wall_width
self.offset = offset
self.viz_color = color
self.viz_color = parse_color(color)
self.graph_nodes = []
self.nav_poses = []
self.is_open = is_open
Expand Down
13 changes: 8 additions & 5 deletions pyrobosim/pyrobosim/core/locations.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from shapely import intersects_xy
from shapely.plotting import patch_from_polygon

from ..utils.general import EntityMetadata, InvalidEntityCategoryException
from ..utils.general import EntityMetadata, InvalidEntityCategoryException, parse_color
from ..utils.pose import Pose, rot2d
from ..utils.polygon import (
inflate_polygon,
Expand Down Expand Up @@ -56,9 +56,12 @@ def __init__(
:type pose: :class:`pyrobosim.utils.pose.Pose`
:param parent: Parent of the location (typically a :class:`pyrobosim.core.room.Room`)
:type parent: Entity
:param color: Visualization color as an (R, G, B) tuple in the range (0.0, 1.0).
If using a category with a defined color, this parameter overrides the category color.
:type color: (float, float, float), optional
:param color: Visualization color. Input can be
- an (R, G, B) tuple, list in the range (0.0, 1.0),
- a string (e.g., "red")
- a hexadecimal (e.g., "#FF0000").
If using a category with a defined color, this parameter overrides the category color.
:type color: list[float] | tuple[float, float, float] | str
:param is_open: If True, the location is open, otherwise it is closed.
:type is_open: bool, optional
:param is_locked: If True, the location is locked, meaning it cannot be opened or closed.
Expand Down Expand Up @@ -87,7 +90,7 @@ def __init__(
)

if color is not None:
self.viz_color = color
self.viz_color = parse_color(color)
elif "color" in self.metadata:
self.viz_color = self.metadata["color"]

Expand Down
13 changes: 8 additions & 5 deletions pyrobosim/pyrobosim/core/objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from shapely.plotting import patch_from_polygon
from scipy.spatial import ConvexHull

from ..utils.general import EntityMetadata, InvalidEntityCategoryException
from ..utils.general import EntityMetadata, InvalidEntityCategoryException, parse_color
from ..utils.pose import Pose
from ..utils.polygon import (
convhull_to_rectangle,
Expand Down Expand Up @@ -57,9 +57,12 @@ def __init__(
:type pose: :class:`pyrobosim.utils.pose.Pose`
:param inflation_radius: Inflation radius for polygon collision checks.
:type inflation_radius: float, optional
:param color: Visualization color as an (R, G, B) tuple in the range (0.0, 1.0).
If using a category with a defined color, this parameter overrides the category color.
:type color: (float, float, float), optional
:param color: Visualization color. Input can be
- an (R, G, B) tuple, list in the range (0.0, 1.0),
- a string (e.g., "red")
- a hexadecimal (e.g., "#FF0000").
If using a category with a defined color, this parameter overrides the category color.
:type color: list[float] | tuple[float, float, float] | str
"""
self.category = category
self.name = name
Expand All @@ -77,7 +80,7 @@ def __init__(
)

if color is not None:
self.viz_color = color
self.viz_color = parse_color(color)
elif "color" in self.metadata:
self.viz_color = self.metadata["color"]

Expand Down
10 changes: 7 additions & 3 deletions pyrobosim/pyrobosim/core/robot.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from ..utils.logging import create_logger
from ..utils.polygon import sample_from_polygon, transform_polygon
from ..utils.pose import Pose
from ..utils.general import parse_color


class Robot:
Expand Down Expand Up @@ -47,8 +48,11 @@ def __init__(
:type radius: float, optional
:param height: Robot height, in meters.
:type height: float, optional
:param color: Robot color, as an RGB tuple or string.
:type color: tuple[float] / str, optional
:param color: Visualization color. Input can be
- an (R, G, B) tuple, list in the range (0.0, 1.0),
- a string (e.g., "red")
- a hexadecimal (e.g., "#FF0000").
:type color: list[float] | tuple[float, float, float] | str
:param max_linear_velocity: The maximum linear velocity magnitude, in m/s.
:type max_linear_velocity: float
:param max_angular_velocity: The maximum angular velocity magnitude, in rad/s.
Expand Down Expand Up @@ -78,7 +82,7 @@ def __init__(
self.name = name
self.radius = radius
self.height = height
self.color = color
self.color = parse_color(color)

if name == "world":
raise ValueError("Robots cannot be named 'world'.")
Expand Down
3 changes: 3 additions & 0 deletions pyrobosim/pyrobosim/data/test_world.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ robots:
- name: robot
radius: 0.1
location: kitchen
color: "#CC00CC"
pose:
position:
x: 0.0
Expand Down Expand Up @@ -104,6 +105,7 @@ hallways:
conn_method: auto
is_open: True
is_locked: False
color: "#666666"

- room_start: bathroom
room_end: bedroom
Expand All @@ -113,6 +115,7 @@ hallways:
offset: 0.8
is_open: True
is_locked: False
color: "dimgray"

- room_start: kitchen
room_end: bedroom
Expand Down
4 changes: 3 additions & 1 deletion pyrobosim/pyrobosim/data/test_world_multirobot.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ robots:
dt: 0.1

- radius: 0.08
color: [0.8, 0.8, 0.0]
color: "#CCCC00"
location: bathroom
grasping:
generator: parallel_grasp
Expand Down Expand Up @@ -136,6 +136,7 @@ hallways:
conn_method: auto
is_open: True
is_locked: False
color: "#666666"

- room_start: bathroom
room_end: bedroom
Expand All @@ -145,6 +146,7 @@ hallways:
offset: 0.8
is_open: True
is_locked: False
color: "dimgray"

- room_start: kitchen
room_end: bedroom
Expand Down
12 changes: 8 additions & 4 deletions pyrobosim/pyrobosim/utils/general.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,12 @@ def parse_color(color):
:return: RGB tuple in range (0.0, 1.0).
:rtype: tuple[float, float, float]
"""
if isinstance(color, (list, tuple)) and len(color) == 3:
return tuple(color)
if isinstance(color, (list, tuple)):
if len(color) == 3:
return tuple(color)
raise ValueError(
"Insufficient elements. RGB color must have exactly 3 elements."
sea-bass marked this conversation as resolved.
Show resolved Hide resolved
)

if isinstance(color, str):
if color in CSS4_COLORS:
Expand All @@ -123,8 +127,8 @@ def parse_color(color):
if re.match(hex_pattern, color):
return to_rgb(color)

raise ValueError(f"Invalid color string or hex: {color}")
raise ValueError(f"Invalid color name or hexadecimal value: {color}.")

raise ValueError(
f"Unsupported color format. Supported types are list[float] and string"
"Unsupported input type. Expected a list, tuple, or string representing a color."
)
3 changes: 2 additions & 1 deletion pyrobosim/test/core/test_yaml_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ def test_create_hallways_from_yaml():
assert loader.world.hallways[0].room_end.name == "bedroom"
assert loader.world.hallways[0].width == 0.75
assert loader.world.hallways[0].wall_width == 0.18
assert loader.world.hallways[0].viz_color == [0.5, 0.5, 0.5]
assert loader.world.hallways[0].viz_color == (0.5, 0.5, 0.5)

@staticmethod
@pytest.mark.dependency(
Expand Down Expand Up @@ -414,6 +414,7 @@ def test_create_robots_from_yaml():
assert robot1.name == "test_robot"
assert robot1.radius == 0.09
assert robot1.height == 0.05
assert robot1.color == (0.0, 0.8, 0.8)
assert robot1.location.name == "bedroom"
assert robot1.get_pose() == Pose.from_list([2.5, 3.0, 1.57])
assert np.all(robot1.dynamics.vel_limits == np.array([1.0, 1.0, 3.0]))
Expand Down
27 changes: 22 additions & 5 deletions pyrobosim/test/utils/test_general_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,38 @@ def test_parse_color():
assert parse_color("#00FFFF") == to_rgb(color_hex)

# Test with invalid RGB list
with pytest.raises(ValueError):
with pytest.raises(ValueError) as exc_info:
parse_color([1.0, 0.0])
assert (
exc_info.value.args[0]
== "Insufficient elements. RGB color must have exactly 3 elements."
)

# Test with invalid RGB tuple
with pytest.raises(ValueError):
with pytest.raises(ValueError) as exc_info:
parse_color((1.0, 0.0))
assert (
exc_info.value.args[0]
== "Insufficient elements. RGB color must have exactly 3 elements."
)

# Test with invalid named color
with pytest.raises(ValueError):
with pytest.raises(ValueError) as exc_info:
parse_color("notavalidcolor")
assert (
exc_info.value.args[0]
== "Invalid color name or hexadecimal value: notavalidcolor."
)

# Test with invalid hexadecimal color format
with pytest.raises(ValueError):
with pytest.raises(ValueError) as exc_info:
parse_color("#ZZZ")
assert exc_info.value.args[0] == "Invalid color name or hexadecimal value: #ZZZ."

# Test with unsupported input type
with pytest.raises(ValueError):
with pytest.raises(ValueError) as exc_info:
parse_color(12345)
assert (
exc_info.value.args[0]
== "Unsupported input type. Expected a list, tuple, or string representing a color."
)
Loading