Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
3619e2a
Fix typos in README and pyproject.toml
flferretti May 15, 2025
abb8c20
Refactor `UrdfExporter` class
flferretti May 15, 2025
3d46b7e
Refactor `KinematicTree` class
flferretti May 15, 2025
d739bb9
Refactor `KinematicTree` class
flferretti Sep 29, 2025
b87e7af
Move imports to top-level
flferretti Sep 29, 2025
76efdbf
Simplify `switch_frame_convention`
flferretti Sep 29, 2025
3e2c8a2
Optimize Gazebo SDF with caching
flferretti Sep 29, 2025
63ff86c
Update environment YAML
flferretti Sep 29, 2025
f971ea1
Switch to `hatchling` build
flferretti Sep 29, 2025
6cbb479
Improve rod CLI
flferretti Sep 29, 2025
c9463c7
Update .gitignore
flferretti Sep 29, 2025
d76de30
Add back continuous joint fix
flferretti Mar 27, 2026
2ae0f29
Use deque for BFS queue
flferretti Mar 27, 2026
7da7a69
Use cached nodes property for integer/slice indexing
flferretti Mar 27, 2026
9efc5b8
Remove redundant deepcopy in TreeTransforms.build
flferretti Mar 27, 2026
5d7a051
Pre-allocate _ZERO_POSE constant for allclose comparisons
flferretti Mar 27, 2026
3227664
Use next() for canonical link lookup instead of full dict
flferretti Mar 27, 2026
76450b1
Add _fmt() helper to replace scattered map/join patterns
flferretti Mar 27, 2026
fbdb1c5
Cache model.links() to avoid duplicate iteration in frame convention
flferretti Mar 27, 2026
f6e0f1c
Fix remove_edge parent pointer when keep_parent=True
flferretti Mar 27, 2026
b7e6e09
Only emit limit element when it has attributes
flferretti Mar 27, 2026
2cad36e
Remove unused _process_cache and _max_cache_size attributes
flferretti Mar 27, 2026
31d8c97
Drop redundant MD5 hash from lru_cache key in _get_cached_processing
flferretti Mar 27, 2026
8c6ae05
Use tempfile.mkstemp for safe temp file creation
flferretti Mar 27, 2026
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: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,9 @@ dmypy.json
# pixi environments
.pixi
*.egg-info

# Hatch-VCS version file
src/rod/_version.py

# VS Code settings
.vscode/
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ This will automatically install `sdformat` and `gz-tools`.
<details>
<summary>Using pixi</summary>

[`pixi`](https://pixi.sh) definetly provides the quickest way to start using ROD. You can run the tests by executing:
[`pixi`](https://pixi.sh) definitely provides the quickest way to start using ROD. You can run the tests by executing:

```bash
pixi run test
Expand Down
14 changes: 13 additions & 1 deletion environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ name: rod
channels:
- conda-forge
dependencies:
# Core runtime dependencies
- coloredlogs
- mashumaro
- numpy
Expand All @@ -10,11 +11,22 @@ dependencies:
- scipy
- trimesh
- xmltodict

# Development and testing
- black
- isort
- pytest
- pytest-icdiff

# Optional dependencies
- pptree
- idyntree
- pytest
- robot_descriptions

# Gazebo/SDF processing
- libgz-tools2
- libsdformat13

# Python packaging
- hatchling
- hatch-vcs
4 changes: 2 additions & 2 deletions pixi.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 13 additions & 9 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ dependencies = [
"xmltodict",
]

[project.scripts]
rod = "rod.__main__:main"

[project.optional-dependencies]
style = [
"black ~= 24.0",
Expand Down Expand Up @@ -87,18 +90,19 @@ Tracker = "https://github.com/ami-iit/rod/issues"
# ===========

[build-system]
build-backend = "setuptools.build_meta"
build-backend = "hatchling.build"
requires = [
"setuptools>=64",
"setuptools-scm[toml]>=8",
"wheel",
"hatchling", "hatch-vcs"
]

[tool.setuptools]
package-dir = { "" = "src" }
[tool.hatch.version]
source = "vcs"

[tool.hatch.build.hooks.vcs]
version-file = "src/rod/_version.py"

[tool.setuptools_scm]
local_scheme = "dirty-tag"
[tool.hatch.build.targets.wheel]
packages = ["src/rod"]

# =================
# Style and testing
Expand Down Expand Up @@ -158,7 +162,7 @@ ignore = [
"E731", # Do not assign a `lambda` expression, use a `def`
"E741", # Ambiguous variable name
"I001", # Import block is unsorted or unformatted
"RUF003", # Ambigous unicode character in comment
"RUF003", # Ambiguous unicode character in comment
]

[tool.ruff.lint.per-file-ignores]
Expand Down
3 changes: 0 additions & 3 deletions setup.py

This file was deleted.

5 changes: 5 additions & 0 deletions src/rod/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,8 @@ def check_compatible_sdformat(specification_version: str) -> None:

check_compatible_sdformat(specification_version="1.10")
del check_compatible_sdformat

try:
from rod._version import __version__
except ImportError:
__version__ = "unknown"
42 changes: 32 additions & 10 deletions src/rod/__main__.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,62 @@
import argparse
import importlib.metadata
import logging
import sys
from typing import NoReturn

from rod import logging as rodlogging

try:
from rod._version import __version__
except ImportError:
# Fallback for development installations
try:
import importlib.metadata

def main() -> None:
__version__ = importlib.metadata.version("rod")
except importlib.metadata.PackageNotFoundError:
__version__ = "unknown"


def main() -> NoReturn:
"""
Main function of the ROD command line interface.
"""
parser = argparse.ArgumentParser(
prog="rod",
description="ROD: The ultimate Python tool for RObot Descriptions processing.",
usage="%(prog)s [options] file",
description="ROD: The ultimate Python tool for RObot Descriptions processing.\n"
"Load, parse, convert, and manipulate robot description files (SDF/URDF).",
epilog="Examples:\n"
" rod -f robot.urdf --show # Display robot model structure\n"
" rod -f robot.sdf -o robot.urdf # Convert SDF to URDF\n"
" rod -f robot.urdf -o robot.sdf # Convert URDF to SDF\n"
" rod --version # Show version information",
formatter_class=argparse.RawDescriptionHelpFormatter,
)

# Version.
parser.add_argument(
"-V",
"--version",
action="version",
version=f"%(prog)s {importlib.metadata.version('rod')}",
version=f"%(prog)s {__version__}",
help="Show version information and exit.",
)

# Verbose output.
parser.add_argument(
"-vv",
"--verbose",
action="store_true",
help="enable verbose output.",
help="Enable verbose output with detailed logging information.",
)

# File to parse.
parser.add_argument(
"-f",
"--file",
type=str,
help="path to the file to parse.",
metavar="PATH",
help="Path to the robot description file to parse (supports .sdf, .urdf).",
required=False,
)

Expand All @@ -46,21 +65,21 @@ def main() -> None:
"-s",
"--show",
action="store_true",
help="show the robot model attributes.",
help="Display the parsed robot model structure and properties in a human-readable format.",
)

# Option to output a URDF or SDF file.
parser.add_argument(
"-o",
"--output",
type=str,
help="Output file path.",
metavar="PATH",
help="Output file path for format conversion (.urdf or .sdf extension required).",
)

args = parser.parse_args()

log_level = logging.DEBUG if args.verbose else logging.INFO

logging.basicConfig(level=log_level)

from rod.urdf.exporter import UrdfExporter
Expand Down Expand Up @@ -110,6 +129,9 @@ def main() -> None:
rodlogging.exception(f"Error writing output file: {e}")
sys.exit(1)

# Exit successfully if we reach here
sys.exit(0)


if __name__ == "__main__":
main()
Loading
Loading