irsim.config.palette_param 源代码

"""
Default colors of everything IR-SIM draws.

The values are Okabe-Ito based (with a second green from Paul Tol's muted
scheme), so they stay distinguishable under common forms of colour-vision
deficiency and keep robots, sensors and obstacles apart in grayscale print.
Any color given in YAML or code overrides them. They are read when an object
or a plot is created, so changing them before ``irsim.make()`` restyles every
following scene::

    from irsim.config import palette_param

    palette_param.robot = "#0072B2"
    palette_param.cycle = ["#0072B2", "#D55E00", "#009E73"]

Attributes:
    robot: Robots with ``diff``, ``omni``, ``omni_angular`` or custom kinematics.
    robot_acker: Car-like (``acker``) robots.
    obstacle: Obstacles, grid maps and any object without a role color.
    pushable: Pushable objects (finite ``mass``, not static) without
        kinematics, which only move when ``collision_mode: contact`` pushes
        them, so they stand out from static obstacles.
    arrow: Heading arrow drawn on top of a body.
    fov: Field-of-view fill.
    fov_edge: Field-of-view outline.
    lidar: Lidar beams.
    laser_highlight: Beams singled out with ``set_laser_color``.
    fmcw_zero_velocity: FMCW beams with no radial velocity.
    fmcw_positive_velocity: FMCW beams moving away.
    fmcw_negative_velocity: FMCW beams moving closer.
    marker: Points drawn with ``env.draw_points``.
    path: Lines drawn with ``env.draw_trajectory`` and ``env.draw_box``.
    quiver: Arrows drawn with ``env.draw_quiver``.
    cycle: Colors handed out in turn to the objects of a group configured with
        ``color: 'cycle'``; it starts with the robot color so a single robot
        looks the same either way.
"""

import sys
from dataclasses import dataclass, field, fields
from types import ModuleType

DEFAULT_CYCLE = (
    "#009E73",  # bluish green (the robot color)
    "#0072B2",  # blue
    "#E69F00",  # orange
    "#CC79A7",  # reddish purple
    "#56B4E9",  # sky blue
    "#D55E00",  # vermillion
    "#F0E442",  # yellow
    "#117733",  # dark green
)


[文档] @dataclass class PaletteParam: """Default colors, one attribute per kind of drawing (see the module docstring).""" robot: str = "#009E73" robot_acker: str = "#117733" obstacle: str = "k" pushable: str = "#E69F00" arrow: str = "#F0E442" fov: str = "#56B4E9" fov_edge: str = "#0072B2" lidar: str = "#CC3311" laser_highlight: str = "#56B4E9" fmcw_zero_velocity: str = "#56B4E9" fmcw_positive_velocity: str = "#CC3311" fmcw_negative_velocity: str = "#0072B2" marker: str = "#CC79A7" path: str = "#0072B2" quiver: str = "k" cycle: list[str] = field(default_factory=lambda: list(DEFAULT_CYCLE))
[文档] def cycle_color(self, index: int) -> str: """Color of the ``index``-th object of a group drawn with ``color: 'cycle'``.""" return self.cycle[index % len(self.cycle)]
# Multi-instance storage (default index 0), like the other config modules. _instances: list[PaletteParam] = [PaletteParam()] _current = _instances[0] _PARAM_FIELDS = frozenset(f.name for f in fields(PaletteParam))
[文档] def bind(instance: PaletteParam) -> None: """Bind instance to default index 0 and update current alias.""" global _current if _instances: _instances[0] = instance else: _instances.append(instance) _current = instance
class _ParamModule(ModuleType): """Route param-field access on the module to the bound instance. PEP 562 lets a module define ``__getattr__`` only; plain assignment (``palette_param.robot = x``) would create a real module attribute that permanently shadows the proxy. Swapping the module class makes attribute reads, writes, and index access all resolve against the currently bound :class:`PaletteParam` instance. """ def __getattr__(self, name: str): return getattr(_current, name) def __setattr__(self, name: str, value) -> None: if name in _PARAM_FIELDS: setattr(_current, name, value) else: super().__setattr__(name, value) def __getitem__(self, index: int) -> PaletteParam: return _instances[index] def __setitem__(self, index: int, instance: PaletteParam) -> None: """Assign a PaletteParam at a specific index. Extends list if needed.""" global _current if index < 0: raise IndexError("palette_param index must be non-negative") if index >= len(_instances): _instances.extend( PaletteParam() for _ in range(index - len(_instances) + 1) ) _instances[index] = instance if index == 0: _current = instance sys.modules[__name__].__class__ = _ParamModule