From 5cfdad79470a890deaac9a00149e24983c63cd73 Mon Sep 17 00:00:00 2001 From: Robertleoj Date: Thu, 19 Dec 2024 14:41:18 +0100 Subject: [PATCH 1/2] update pyroject and lint/format --- notebooks/dynamics.py | 56 +- poetry.lock | 1946 +++++++++++++++++ pyproject.toml | 250 +-- roboticstoolbox/__init__.py | 1 + roboticstoolbox/backends/Connector.py | 35 +- .../backends/Dynamixel/dynamixel_io.py | 281 ++- .../Dynamixel/dynamixel_sdk/__init__.py | 12 +- .../dynamixel_sdk/group_bulk_read.py | 40 +- .../dynamixel_sdk/group_bulk_write.py | 2 +- .../dynamixel_sdk/group_sync_read.py | 45 +- .../dynamixel_sdk/group_sync_write.py | 11 +- .../Dynamixel/dynamixel_sdk/packet_handler.py | 4 +- .../Dynamixel/dynamixel_sdk/port_handler.py | 32 +- .../dynamixel_sdk/protocol1_packet_handler.py | 88 +- .../dynamixel_sdk/protocol2_packet_handler.py | 489 ++++- roboticstoolbox/backends/Dynamixel/dyndata.py | 69 +- .../backends/PyPlot/EllipsePlot.py | 2 +- roboticstoolbox/backends/PyPlot/PyPlot.py | 3 +- roboticstoolbox/backends/PyPlot/PyPlot2.py | 5 - roboticstoolbox/backends/PyPlot/RobotPlot2.py | 59 +- roboticstoolbox/backends/PyPlot/__init__.py | 5 +- roboticstoolbox/backends/ROS/ROS.py | 43 +- roboticstoolbox/backends/ROS/__init__.py | 4 +- roboticstoolbox/backends/swift/__init__.py | 33 +- roboticstoolbox/bin/rtbtool.py | 32 +- roboticstoolbox/blocks/arm.py | 3 +- roboticstoolbox/blocks/mobile.py | 7 +- roboticstoolbox/blocks/spatial.py | 10 +- roboticstoolbox/blocks/uav.py | 23 +- roboticstoolbox/examples/RRMC.py | 3 +- roboticstoolbox/examples/RRMC_swift.py | 1 - roboticstoolbox/examples/baur.py | 17 +- roboticstoolbox/examples/branched_robot.py | 1 - roboticstoolbox/examples/eigdemo.py | 54 +- roboticstoolbox/examples/fetch_vision.py | 1 - .../examples/holistic_mm_non_holonomic.py | 2 - roboticstoolbox/examples/holistic_mm_omni.py | 2 - roboticstoolbox/examples/ik_exp.py | 10 - roboticstoolbox/examples/ik_speed.py | 8 - roboticstoolbox/examples/ikine_evaluate.py | 60 +- roboticstoolbox/examples/ikine_evaluate2.py | 72 +- roboticstoolbox/examples/mmc.py | 1 - roboticstoolbox/examples/neo.py | 1 - roboticstoolbox/examples/park.py | 5 +- roboticstoolbox/examples/plot.py | 2 +- roboticstoolbox/examples/readme.py | 12 +- roboticstoolbox/examples/swift_recording.py | 5 +- roboticstoolbox/examples/teach_swift.py | 1 - roboticstoolbox/examples/test.py | 1 - roboticstoolbox/examples/tripleangledemo.py | 2 +- roboticstoolbox/examples/twistdemo.py | 80 +- roboticstoolbox/examples/vehicle1.py | 1 - roboticstoolbox/mobile/Animations.py | 5 +- .../mobile/DistanceTransformPlanner.py | 12 +- roboticstoolbox/mobile/DstarPlanner.py | 9 +- roboticstoolbox/mobile/DubinsPlanner.py | 4 +- roboticstoolbox/mobile/EKF.py | 7 +- roboticstoolbox/mobile/LatticePlanner.py | 6 - roboticstoolbox/mobile/OccGrid.py | 3 - roboticstoolbox/mobile/PRMPlanner.py | 3 +- roboticstoolbox/mobile/ParticleFilter.py | 4 +- roboticstoolbox/mobile/PlannerBase.py | 3 - roboticstoolbox/mobile/PoseGraph.py | 13 +- roboticstoolbox/mobile/QuinticPolyPlanner.py | 11 +- roboticstoolbox/mobile/RRTPlanner.py | 3 +- roboticstoolbox/mobile/ReedsSheppPlanner.py | 6 +- roboticstoolbox/mobile/Vehicle.py | 13 +- roboticstoolbox/mobile/drivers.py | 15 +- roboticstoolbox/mobile/landmarkmap.py | 7 +- roboticstoolbox/mobile/sensors.py | 6 +- roboticstoolbox/models/DH/AL5D.py | 2 - roboticstoolbox/models/DH/Ball.py | 2 - roboticstoolbox/models/DH/Baxter.py | 2 - roboticstoolbox/models/DH/Coil.py | 2 - roboticstoolbox/models/DH/Hyper.py | 5 - roboticstoolbox/models/DH/Hyper3d.py | 2 - roboticstoolbox/models/DH/IRB140.py | 11 +- roboticstoolbox/models/DH/Jaco.py | 4 +- roboticstoolbox/models/DH/KR5.py | 15 +- roboticstoolbox/models/DH/LWR4.py | 2 - roboticstoolbox/models/DH/Mico.py | 5 +- roboticstoolbox/models/DH/Orion5.py | 2 - roboticstoolbox/models/DH/P8.py | 3 - roboticstoolbox/models/DH/Panda.py | 2 - roboticstoolbox/models/DH/Planar2.py | 2 - roboticstoolbox/models/DH/Planar3.py | 2 - roboticstoolbox/models/DH/Puma560.py | 7 +- roboticstoolbox/models/DH/Sawyer.py | 2 - roboticstoolbox/models/DH/Stanford.py | 2 - roboticstoolbox/models/DH/TwoLink.py | 3 +- roboticstoolbox/models/DH/UR10.py | 3 - roboticstoolbox/models/DH/UR3.py | 3 - roboticstoolbox/models/DH/UR5.py | 3 - roboticstoolbox/models/DH/__init__.py | 48 +- roboticstoolbox/models/ETS/Frankie.py | 2 - roboticstoolbox/models/ETS/GenericSeven.py | 1 - roboticstoolbox/models/ETS/Omni.py | 2 - roboticstoolbox/models/ETS/Panda.py | 2 - roboticstoolbox/models/ETS/Planar2.py | 4 +- roboticstoolbox/models/ETS/Planar_Y.py | 2 - roboticstoolbox/models/ETS/Puma560.py | 1 - roboticstoolbox/models/ETS/XYPanda.py | 1 - roboticstoolbox/models/URDF/AL5D.py | 3 - roboticstoolbox/models/URDF/Fetch.py | 2 - roboticstoolbox/models/URDF/FetchCamera.py | 2 - roboticstoolbox/models/URDF/Frankie.py | 2 - roboticstoolbox/models/URDF/FrankieOmni.py | 1 - roboticstoolbox/models/URDF/KinovaGen3.py | 2 - roboticstoolbox/models/URDF/LBR.py | 2 - roboticstoolbox/models/URDF/Mico.py | 2 - roboticstoolbox/models/URDF/PR2.py | 2 - roboticstoolbox/models/URDF/Panda.py | 2 - roboticstoolbox/models/URDF/Puma560.py | 2 - roboticstoolbox/models/URDF/UR10.py | 2 - roboticstoolbox/models/URDF/UR3.py | 2 - roboticstoolbox/models/URDF/UR5.py | 2 - roboticstoolbox/models/URDF/Valkyrie.py | 6 +- roboticstoolbox/models/URDF/YuMi.py | 2 - roboticstoolbox/models/URDF/px100.py | 2 - roboticstoolbox/models/URDF/px150.py | 2 - roboticstoolbox/models/URDF/rx150.py | 2 - roboticstoolbox/models/URDF/rx200.py | 2 - roboticstoolbox/models/URDF/vx300.py | 2 - roboticstoolbox/models/URDF/vx300s.py | 2 - roboticstoolbox/models/URDF/wx200.py | 2 - roboticstoolbox/models/URDF/wx250.py | 2 - roboticstoolbox/models/URDF/wx250s.py | 2 - roboticstoolbox/models/list.py | 6 +- roboticstoolbox/robot/DHFactor.py | 9 +- roboticstoolbox/robot/DHLink.py | 8 +- roboticstoolbox/robot/DHRobot.py | 6 +- roboticstoolbox/robot/Dynamics.py | 12 +- roboticstoolbox/robot/ERobot.py | 2 - roboticstoolbox/robot/ETS.py | 42 +- roboticstoolbox/robot/Gripper.py | 2 - roboticstoolbox/robot/Link.py | 35 +- roboticstoolbox/robot/PoERobot.py | 4 +- roboticstoolbox/robot/Robot.py | 26 +- roboticstoolbox/robot/RobotProto.py | 58 +- roboticstoolbox/tools/DHFactor.py | 158 +- roboticstoolbox/tools/Ticker.py | 7 +- roboticstoolbox/tools/data.py | 31 +- roboticstoolbox/tools/jsingu.py | 7 +- roboticstoolbox/tools/null.py | 4 +- roboticstoolbox/tools/numerical.py | 27 +- roboticstoolbox/tools/p_servo.py | 1 - roboticstoolbox/tools/params.py | 3 +- roboticstoolbox/tools/trajectory.py | 7 +- roboticstoolbox/tools/urdf/__init__.py | 50 +- roboticstoolbox/tools/urdf/tests/test_urdf.py | 80 +- roboticstoolbox/tools/urdf/urdf.py | 6 +- roboticstoolbox/tools/urdf/utils.py | 14 +- roboticstoolbox/tools/xacro/__init__.py | 438 ++-- roboticstoolbox/tools/xacro/cli.py | 61 +- roboticstoolbox/tools/xacro/color.py | 18 +- .../tools/xacro/tests/test_xacro.py | 957 ++++---- roboticstoolbox/tools/xacro/xmlutils.py | 25 +- rtb-data/setup.py | 2 +- setup.py | 2 +- tests/examples.py | 9 +- tests/test_BaseRobot.py | 10 - tests/test_CustomXacro.py | 2 - tests/test_ERobot.py | 2 - tests/test_ET.py | 1 - tests/test_ETS.py | 3 +- tests/test_ETS2.py | 2 - tests/test_IK.py | 36 - tests/test_Link.py | 2 - tests/test_PyPlot.py | 1 - tests/test_PyPlot2.py | 10 +- tests/test_Ticker.py | 3 +- tests/test_blocks.py | 33 +- tests/test_mobile.py | 8 - tests/test_mobile_planner.py | 54 +- tests/test_models.py | 1 - tests/test_tools.py | 1 - tests/test_trajectory.py | 379 +++- 177 files changed, 4659 insertions(+), 2271 deletions(-) create mode 100644 poetry.lock diff --git a/notebooks/dynamics.py b/notebooks/dynamics.py index f95dd525d..d9fc70e9b 100644 --- a/notebooks/dynamics.py +++ b/notebooks/dynamics.py @@ -6,11 +6,15 @@ import numpy as np import roboticstoolbox as rtb -from spatialmath import * # lgtm [py/polluting-import] +from spatialmath import * # lgtm [py/polluting-import] from math import pi + # import matplotlib.pyplot as plt # from matplotlib import cm -np.set_printoptions(linewidth=100, formatter={'float': lambda x: f"{x:8.4g}" if x > 1e-10 else f"{0:8.4g}"}) +np.set_printoptions( + linewidth=100, + formatter={"float": lambda x: f"{x:8.4g}" if x > 1e-10 else f"{0:8.4g}"}, +) block = False @@ -38,7 +42,7 @@ # - the inertia or mass matrix $\mathbf{M}(\mathit{\ddot{q}})$ which is a function of joint configuration # - the centripetal and Coriolis or velocity term which is a function of joint configuration and rate # - the gravity load which is a function of joint configuration -# +# # If the robot is not moving, that is $\mathit{q} = \mathit{\dot{q}} = 0$ then the equation becomes # $$ # \mathbf{g}(\mathit{q}) = \mathit{\tau} @@ -46,26 +50,26 @@ # where $\mathit{\tau}$ is the torque required for this condition $\mathit{q} = \mathit{\dot{q}} = 0$ to be true, that is, the torque required to stop the robot falling under its own weight. The toolbox can compute this # In[4]: -print('about to do coriolis') +print("about to do coriolis") p560.gravload(p560.qn) -print('done gravload') +print("done gravload") # and it shows, as expected, that the shoulder is exerting significant torque to hold the arm up and stationary. -# +# # The inertia matrix relates torque to joint acceleration and is the mass in a multi-dimensional version of Newton's second law $F = m a$. In this configuration the inertia matrix is # In[5]: p560.inertia(p560.qn) -print('done inertia') +print("done inertia") # The diagonal elements $M_{jj}$ indicate the inertia experienced by the joint $j$, ie. Newton's second law for this joint is $\tau_j = M_{jj} \ddot{q}_j$. -# +# # The matrix is symmetric and the off-diagonal terms $M_{ij} = M_{ji}$ couple acceleration of one joint into a disturbance torque on another joint, ie. $\tau_j = M_{ij} \ddot{q}_i$. -# +# # The inertia matrix is a function of joint configuration, that is, the elements of the inertia matrix change as we vary the angles of joints 1 and 2, ie. $q_2$ and $q_3$. It is this configuration varying inertia and coupling between joints that is a fundamental challenge for high-quality joint control. # In[6]: @@ -73,13 +77,13 @@ N = 100 (Q2, Q3) = np.meshgrid(np.linspace(-pi, pi, N), np.linspace(-pi, pi, N)) -M11 = np.zeros((N,N)) -M12 = np.zeros((N,N)) +M11 = np.zeros((N, N)) +M12 = np.zeros((N, N)) for i in range(N): for j in range(N): - M = p560.inertia(np.r_[0, Q2[i,j], Q3[i,j], 0, 0, 0]) - M11[i,j] = M[0,0] - M12[i,j] = M[0,1] + M = p560.inertia(np.r_[0, Q2[i, j], Q3[i, j], 0, 0, 0]) + M11[i, j] = M[0, 0] + M12[i, j] = M[0, 1] # The inertia "seen" by joint 1 varies as a function of $q_2$ and $q_3$ as shown below @@ -103,7 +107,7 @@ M11.max() / M11.min() -print('done inertias') +print("done inertias") # The coupling inertia between joints 1 and 2 also varies with configuration and we can plot that as well @@ -119,7 +123,7 @@ # plt.show(block=True) -# The velocity terms are a bit harder to comprehend but they mean that rotation of one joint (and its link) can exert a torque on other joints. Consider that the should joint is rotating at 1 rad/sec, then the torque will be +# The velocity terms are a bit harder to comprehend but they mean that rotation of one joint (and its link) can exert a torque on other joints. Consider that the should joint is rotating at 1 rad/sec, then the torque will be # In[10]: @@ -127,11 +131,11 @@ qd = np.r_[0, 1, 0, 0, 0, 0] p560.coriolis(p560.qn, qd) @ qd -print('done coriolis') +print("done coriolis") # and we see that it exerts a torque on the waist and elbow joints. -# +# # The algorithms to compute the various terms in the rigid-body equations of motion are based on the recursive Newton-Euler algorithm # In[ ]: @@ -139,19 +143,19 @@ p560.rne(p560.qn, np.zeros((6,)), np.zeros((6,))) -print('done rne') +print("done rne") # which computes $\tau = \mbox{rne}(\mathit{q}, \mathit{\dot{q}}, \mathit{\dot{q}})$ and can accept additional arguments such as gravity or a wrench applied to the end-effector. In the Toolbox this algorithm is implemented in C for maximum performance. -# +# # The example above computes the gravity load, cross check it with the result computed earlier. -# +# # We can rearrange the equations of motion as # $$ -# \mathit{\ddot{q}} = \mathbf{M}(\mathit{\ddot{q}})^{-1} \left\{ \mathit{\tau} - \mathbf{C}(\mathit{q}, \mathit{\dot{q}}) \mathit{\dot{q}} - \mathbf{g}(\mathit{q}) \right\} +# \mathit{\ddot{q}} = \mathbf{M}(\mathit{\ddot{q}})^{-1} \left\{ \mathit{\tau} - \mathbf{C}(\mathit{q}, \mathit{\dot{q}}) \mathit{\dot{q}} - \mathbf{g}(\mathit{q}) \right\} # $$ # which is the joint acceleration for a robot in the state $(\mathit{q}, \mathit{\dot{q}})$ with an applied torque of $\mathit{\tau}$. -# +# # If we integrate this we can compute the trajectory of the joint coordinates (and velocities) as a function of time # In[5]: @@ -160,15 +164,15 @@ p560nf = p560.nofriction() tg = p560nf.fdyn(5, p560.qn, dt=0.05) -print('done fdyn') +print("done fdyn") # The first line needs some explanation. The Toolbox can model two types of joint friction: # - viscous friction which is linearly related to joint velocity # - Coulomb friction which is **non-linearly** related to joint velocity -# +# # Coulomb friction is a _harsh_ non-linearity and it causes the numerical integrator to take very small times steps, so the result will take many minutes to compute. To speed things up, at the expense of some modeling fidelity, we set the Coulomb friction to zero, but retain the viscous friction. The `nofriction()` method returns a clone of the robot with its friction parameters modified. -# +# # The computed joint configuration trajectory is # In[ ]: diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 000000000..5495d3a5d --- /dev/null +++ b/poetry.lock @@ -0,0 +1,1946 @@ +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. + +[[package]] +name = "alabaster" +version = "1.0.0" +description = "A light, configurable Sphinx theme" +optional = false +python-versions = ">=3.10" +files = [ + {file = "alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b"}, + {file = "alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e"}, +] + +[[package]] +name = "ansitable" +version = "0.11.4" +description = "Quick and easy display of tabular data and matrices with optional ANSI color and borders" +optional = false +python-versions = ">=3.7" +files = [ + {file = "ansitable-0.11.4-py3-none-any.whl", hash = "sha256:592be469b0a4fe2341aba806b4c52c3a12f8614ecfa38f29cfd0c48dbeddb614"}, + {file = "ansitable-0.11.4.tar.gz", hash = "sha256:5d48d756cf7f11395b6edbd8cfc609a82b0fd4115a8ea40a41f48cf91be6e0ed"}, +] + +[package.dependencies] +colored = "*" + +[package.extras] +docs = ["sphinx", "sphinx-autodoc-typehints", "sphinx-autorun", "sphinx-markdown-tables", "sphinx-rtd-theme", "sphinx_favicon", "sphinxcontrib-jsmath"] + +[[package]] +name = "babel" +version = "2.16.0" +description = "Internationalization utilities" +optional = false +python-versions = ">=3.8" +files = [ + {file = "babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b"}, + {file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"}, +] + +[package.extras] +dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] + +[[package]] +name = "bdsim" +version = "1.1.2" +description = "Simulate dynamic systems expressed in block diagram form using Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "bdsim-1.1.2-py3-none-any.whl", hash = "sha256:174060ba74240c452e59b04e32beb4b47191da311b5ccd756f9e6fa0051ce6d8"}, + {file = "bdsim-1.1.2.tar.gz", hash = "sha256:c82b1093a5f639f5a48aa3c77ea47f8f944b4b3617085b18ab2ec1584d67f4d3"}, +] + +[package.dependencies] +ansitable = "*" +matplotlib = "*" +numpy = ">=1.17.4" +progress = "*" +scipy = "*" +spatialmath-python = "*" + +[package.extras] +dev = ["coverage", "flake8", "pytest", "pytest-timeout", "pytest-xvfb", "sympy"] +docs = ["sphinx", "sphinx-autodoc-typehints", "sphinx-autorun", "sphinx-favicon", "sphinx-markdown-tables", "sphinx-rtd-theme", "sphinxcontrib-jsmath"] +edit = ["PIL", "PyQt5"] + +[[package]] +name = "certifi" +version = "2024.12.14" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"}, + {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"}, +] + +[[package]] +name = "cfgv" +version = "3.4.0" +description = "Validate configuration and produce human readable error messages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, + {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.0" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-win32.whl", hash = "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-win32.whl", hash = "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win32.whl", hash = "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca"}, + {file = "charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079"}, + {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, +] + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "colored" +version = "2.2.4" +description = "Simple python library for color and formatting to terminal" +optional = false +python-versions = ">=3.9" +files = [ + {file = "colored-2.2.4-py3-none-any.whl", hash = "sha256:a7069673bd90a35f46cb748d012c17284a0668d2f1c06bc7a51822a2d5ad2112"}, + {file = "colored-2.2.4.tar.gz", hash = "sha256:595e1dd7f3b472ea5f12af21d2fec8a2ea2cf8f9d93e67180197330b26df9b61"}, +] + +[[package]] +name = "contourpy" +version = "1.3.1" +description = "Python library for calculating contours of 2D quadrilateral grids" +optional = false +python-versions = ">=3.10" +files = [ + {file = "contourpy-1.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a045f341a77b77e1c5de31e74e966537bba9f3c4099b35bf4c2e3939dd54cdab"}, + {file = "contourpy-1.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:500360b77259914f7805af7462e41f9cb7ca92ad38e9f94d6c8641b089338124"}, + {file = "contourpy-1.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2f926efda994cdf3c8d3fdb40b9962f86edbc4457e739277b961eced3d0b4c1"}, + {file = "contourpy-1.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:adce39d67c0edf383647a3a007de0a45fd1b08dedaa5318404f1a73059c2512b"}, + {file = "contourpy-1.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abbb49fb7dac584e5abc6636b7b2a7227111c4f771005853e7d25176daaf8453"}, + {file = "contourpy-1.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0cffcbede75c059f535725c1680dfb17b6ba8753f0c74b14e6a9c68c29d7ea3"}, + {file = "contourpy-1.3.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ab29962927945d89d9b293eabd0d59aea28d887d4f3be6c22deaefbb938a7277"}, + {file = "contourpy-1.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:974d8145f8ca354498005b5b981165b74a195abfae9a8129df3e56771961d595"}, + {file = "contourpy-1.3.1-cp310-cp310-win32.whl", hash = "sha256:ac4578ac281983f63b400f7fe6c101bedc10651650eef012be1ccffcbacf3697"}, + {file = "contourpy-1.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:174e758c66bbc1c8576992cec9599ce8b6672b741b5d336b5c74e35ac382b18e"}, + {file = "contourpy-1.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3e8b974d8db2c5610fb4e76307e265de0edb655ae8169e8b21f41807ccbeec4b"}, + {file = "contourpy-1.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:20914c8c973f41456337652a6eeca26d2148aa96dd7ac323b74516988bea89fc"}, + {file = "contourpy-1.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19d40d37c1c3a4961b4619dd9d77b12124a453cc3d02bb31a07d58ef684d3d86"}, + {file = "contourpy-1.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:113231fe3825ebf6f15eaa8bc1f5b0ddc19d42b733345eae0934cb291beb88b6"}, + {file = "contourpy-1.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4dbbc03a40f916a8420e420d63e96a1258d3d1b58cbdfd8d1f07b49fcbd38e85"}, + {file = "contourpy-1.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a04ecd68acbd77fa2d39723ceca4c3197cb2969633836ced1bea14e219d077c"}, + {file = "contourpy-1.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c414fc1ed8ee1dbd5da626cf3710c6013d3d27456651d156711fa24f24bd1291"}, + {file = "contourpy-1.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:31c1b55c1f34f80557d3830d3dd93ba722ce7e33a0b472cba0ec3b6535684d8f"}, + {file = "contourpy-1.3.1-cp311-cp311-win32.whl", hash = "sha256:f611e628ef06670df83fce17805c344710ca5cde01edfdc72751311da8585375"}, + {file = "contourpy-1.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:b2bdca22a27e35f16794cf585832e542123296b4687f9fd96822db6bae17bfc9"}, + {file = "contourpy-1.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0ffa84be8e0bd33410b17189f7164c3589c229ce5db85798076a3fa136d0e509"}, + {file = "contourpy-1.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805617228ba7e2cbbfb6c503858e626ab528ac2a32a04a2fe88ffaf6b02c32bc"}, + {file = "contourpy-1.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ade08d343436a94e633db932e7e8407fe7de8083967962b46bdfc1b0ced39454"}, + {file = "contourpy-1.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:47734d7073fb4590b4a40122b35917cd77be5722d80683b249dac1de266aac80"}, + {file = "contourpy-1.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2ba94a401342fc0f8b948e57d977557fbf4d515f03c67682dd5c6191cb2d16ec"}, + {file = "contourpy-1.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efa874e87e4a647fd2e4f514d5e91c7d493697127beb95e77d2f7561f6905bd9"}, + {file = "contourpy-1.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1bf98051f1045b15c87868dbaea84f92408337d4f81d0e449ee41920ea121d3b"}, + {file = "contourpy-1.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:61332c87493b00091423e747ea78200659dc09bdf7fd69edd5e98cef5d3e9a8d"}, + {file = "contourpy-1.3.1-cp312-cp312-win32.whl", hash = "sha256:e914a8cb05ce5c809dd0fe350cfbb4e881bde5e2a38dc04e3afe1b3e58bd158e"}, + {file = "contourpy-1.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:08d9d449a61cf53033612cb368f3a1b26cd7835d9b8cd326647efe43bca7568d"}, + {file = "contourpy-1.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a761d9ccfc5e2ecd1bf05534eda382aa14c3e4f9205ba5b1684ecfe400716ef2"}, + {file = "contourpy-1.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:523a8ee12edfa36f6d2a49407f705a6ef4c5098de4f498619787e272de93f2d5"}, + {file = "contourpy-1.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece6df05e2c41bd46776fbc712e0996f7c94e0d0543af1656956d150c4ca7c81"}, + {file = "contourpy-1.3.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:573abb30e0e05bf31ed067d2f82500ecfdaec15627a59d63ea2d95714790f5c2"}, + {file = "contourpy-1.3.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9fa36448e6a3a1a9a2ba23c02012c43ed88905ec80163f2ffe2421c7192a5d7"}, + {file = "contourpy-1.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ea9924d28fc5586bf0b42d15f590b10c224117e74409dd7a0be3b62b74a501c"}, + {file = "contourpy-1.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5b75aa69cb4d6f137b36f7eb2ace9280cfb60c55dc5f61c731fdf6f037f958a3"}, + {file = "contourpy-1.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:041b640d4ec01922083645a94bb3b2e777e6b626788f4095cf21abbe266413c1"}, + {file = "contourpy-1.3.1-cp313-cp313-win32.whl", hash = "sha256:36987a15e8ace5f58d4d5da9dca82d498c2bbb28dff6e5d04fbfcc35a9cb3a82"}, + {file = "contourpy-1.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:a7895f46d47671fa7ceec40f31fae721da51ad34bdca0bee83e38870b1f47ffd"}, + {file = "contourpy-1.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:9ddeb796389dadcd884c7eb07bd14ef12408aaae358f0e2ae24114d797eede30"}, + {file = "contourpy-1.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:19c1555a6801c2f084c7ddc1c6e11f02eb6a6016ca1318dd5452ba3f613a1751"}, + {file = "contourpy-1.3.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:841ad858cff65c2c04bf93875e384ccb82b654574a6d7f30453a04f04af71342"}, + {file = "contourpy-1.3.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4318af1c925fb9a4fb190559ef3eec206845f63e80fb603d47f2d6d67683901c"}, + {file = "contourpy-1.3.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:14c102b0eab282427b662cb590f2e9340a9d91a1c297f48729431f2dcd16e14f"}, + {file = "contourpy-1.3.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05e806338bfeaa006acbdeba0ad681a10be63b26e1b17317bfac3c5d98f36cda"}, + {file = "contourpy-1.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4d76d5993a34ef3df5181ba3c92fabb93f1eaa5729504fb03423fcd9f3177242"}, + {file = "contourpy-1.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:89785bb2a1980c1bd87f0cb1517a71cde374776a5f150936b82580ae6ead44a1"}, + {file = "contourpy-1.3.1-cp313-cp313t-win32.whl", hash = "sha256:8eb96e79b9f3dcadbad2a3891672f81cdcab7f95b27f28f1c67d75f045b6b4f1"}, + {file = "contourpy-1.3.1-cp313-cp313t-win_amd64.whl", hash = "sha256:287ccc248c9e0d0566934e7d606201abd74761b5703d804ff3df8935f523d546"}, + {file = "contourpy-1.3.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b457d6430833cee8e4b8e9b6f07aa1c161e5e0d52e118dc102c8f9bd7dd060d6"}, + {file = "contourpy-1.3.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb76c1a154b83991a3cbbf0dfeb26ec2833ad56f95540b442c73950af2013750"}, + {file = "contourpy-1.3.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:44a29502ca9c7b5ba389e620d44f2fbe792b1fb5734e8b931ad307071ec58c53"}, + {file = "contourpy-1.3.1.tar.gz", hash = "sha256:dfd97abd83335045a913e3bcc4a09c0ceadbe66580cf573fe961f4a825efa699"}, +] + +[package.dependencies] +numpy = ">=1.23" + +[package.extras] +bokeh = ["bokeh", "selenium"] +docs = ["furo", "sphinx (>=7.2)", "sphinx-copybutton"] +mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.11.1)", "types-Pillow"] +test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] +test-no-images = ["pytest", "pytest-cov", "pytest-rerunfailures", "pytest-xdist", "wurlitzer"] + +[[package]] +name = "coverage" +version = "7.6.9" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "coverage-7.6.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:85d9636f72e8991a1706b2b55b06c27545448baf9f6dbf51c4004609aacd7dcb"}, + {file = "coverage-7.6.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:608a7fd78c67bee8936378299a6cb9f5149bb80238c7a566fc3e6717a4e68710"}, + {file = "coverage-7.6.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96d636c77af18b5cb664ddf12dab9b15a0cfe9c0bde715da38698c8cea748bfa"}, + {file = "coverage-7.6.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d75cded8a3cff93da9edc31446872d2997e327921d8eed86641efafd350e1df1"}, + {file = "coverage-7.6.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7b15f589593110ae767ce997775d645b47e5cbbf54fd322f8ebea6277466cec"}, + {file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:44349150f6811b44b25574839b39ae35291f6496eb795b7366fef3bd3cf112d3"}, + {file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d891c136b5b310d0e702e186d70cd16d1119ea8927347045124cb286b29297e5"}, + {file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:db1dab894cc139f67822a92910466531de5ea6034ddfd2b11c0d4c6257168073"}, + {file = "coverage-7.6.9-cp310-cp310-win32.whl", hash = "sha256:41ff7b0da5af71a51b53f501a3bac65fb0ec311ebed1632e58fc6107f03b9198"}, + {file = "coverage-7.6.9-cp310-cp310-win_amd64.whl", hash = "sha256:35371f8438028fdccfaf3570b31d98e8d9eda8bb1d6ab9473f5a390969e98717"}, + {file = "coverage-7.6.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:932fc826442132dde42ee52cf66d941f581c685a6313feebed358411238f60f9"}, + {file = "coverage-7.6.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:085161be5f3b30fd9b3e7b9a8c301f935c8313dcf928a07b116324abea2c1c2c"}, + {file = "coverage-7.6.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ccc660a77e1c2bf24ddbce969af9447a9474790160cfb23de6be4fa88e3951c7"}, + {file = "coverage-7.6.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c69e42c892c018cd3c8d90da61d845f50a8243062b19d228189b0224150018a9"}, + {file = "coverage-7.6.9-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0824a28ec542a0be22f60c6ac36d679e0e262e5353203bea81d44ee81fe9c6d4"}, + {file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4401ae5fc52ad8d26d2a5d8a7428b0f0c72431683f8e63e42e70606374c311a1"}, + {file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:98caba4476a6c8d59ec1eb00c7dd862ba9beca34085642d46ed503cc2d440d4b"}, + {file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ee5defd1733fd6ec08b168bd4f5387d5b322f45ca9e0e6c817ea6c4cd36313e3"}, + {file = "coverage-7.6.9-cp311-cp311-win32.whl", hash = "sha256:f2d1ec60d6d256bdf298cb86b78dd715980828f50c46701abc3b0a2b3f8a0dc0"}, + {file = "coverage-7.6.9-cp311-cp311-win_amd64.whl", hash = "sha256:0d59fd927b1f04de57a2ba0137166d31c1a6dd9e764ad4af552912d70428c92b"}, + {file = "coverage-7.6.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:99e266ae0b5d15f1ca8d278a668df6f51cc4b854513daab5cae695ed7b721cf8"}, + {file = "coverage-7.6.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9901d36492009a0a9b94b20e52ebfc8453bf49bb2b27bca2c9706f8b4f5a554a"}, + {file = "coverage-7.6.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abd3e72dd5b97e3af4246cdada7738ef0e608168de952b837b8dd7e90341f015"}, + {file = "coverage-7.6.9-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff74026a461eb0660366fb01c650c1d00f833a086b336bdad7ab00cc952072b3"}, + {file = "coverage-7.6.9-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65dad5a248823a4996724a88eb51d4b31587aa7aa428562dbe459c684e5787ae"}, + {file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:22be16571504c9ccea919fcedb459d5ab20d41172056206eb2994e2ff06118a4"}, + {file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f957943bc718b87144ecaee70762bc2bc3f1a7a53c7b861103546d3a403f0a6"}, + {file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ae1387db4aecb1f485fb70a6c0148c6cdaebb6038f1d40089b1fc84a5db556f"}, + {file = "coverage-7.6.9-cp312-cp312-win32.whl", hash = "sha256:1a330812d9cc7ac2182586f6d41b4d0fadf9be9049f350e0efb275c8ee8eb692"}, + {file = "coverage-7.6.9-cp312-cp312-win_amd64.whl", hash = "sha256:b12c6b18269ca471eedd41c1b6a1065b2f7827508edb9a7ed5555e9a56dcfc97"}, + {file = "coverage-7.6.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:899b8cd4781c400454f2f64f7776a5d87bbd7b3e7f7bda0cb18f857bb1334664"}, + {file = "coverage-7.6.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:61f70dc68bd36810972e55bbbe83674ea073dd1dcc121040a08cdf3416c5349c"}, + {file = "coverage-7.6.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a289d23d4c46f1a82d5db4abeb40b9b5be91731ee19a379d15790e53031c014"}, + {file = "coverage-7.6.9-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e216d8044a356fc0337c7a2a0536d6de07888d7bcda76febcb8adc50bdbbd00"}, + {file = "coverage-7.6.9-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c026eb44f744acaa2bda7493dad903aa5bf5fc4f2554293a798d5606710055d"}, + {file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e77363e8425325384f9d49272c54045bbed2f478e9dd698dbc65dbc37860eb0a"}, + {file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:777abfab476cf83b5177b84d7486497e034eb9eaea0d746ce0c1268c71652077"}, + {file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:447af20e25fdbe16f26e84eb714ba21d98868705cb138252d28bc400381f6ffb"}, + {file = "coverage-7.6.9-cp313-cp313-win32.whl", hash = "sha256:d872ec5aeb086cbea771c573600d47944eea2dcba8be5f3ee649bfe3cb8dc9ba"}, + {file = "coverage-7.6.9-cp313-cp313-win_amd64.whl", hash = "sha256:fd1213c86e48dfdc5a0cc676551db467495a95a662d2396ecd58e719191446e1"}, + {file = "coverage-7.6.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ba9e7484d286cd5a43744e5f47b0b3fb457865baf07bafc6bee91896364e1419"}, + {file = "coverage-7.6.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e5ea1cf0872ee455c03e5674b5bca5e3e68e159379c1af0903e89f5eba9ccc3a"}, + {file = "coverage-7.6.9-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d10e07aa2b91835d6abec555ec8b2733347956991901eea6ffac295f83a30e4"}, + {file = "coverage-7.6.9-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:13a9e2d3ee855db3dd6ea1ba5203316a1b1fd8eaeffc37c5b54987e61e4194ae"}, + {file = "coverage-7.6.9-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c38bf15a40ccf5619fa2fe8f26106c7e8e080d7760aeccb3722664c8656b030"}, + {file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d5275455b3e4627c8e7154feaf7ee0743c2e7af82f6e3b561967b1cca755a0be"}, + {file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8f8770dfc6e2c6a2d4569f411015c8d751c980d17a14b0530da2d7f27ffdd88e"}, + {file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8d2dfa71665a29b153a9681edb1c8d9c1ea50dfc2375fb4dac99ea7e21a0bcd9"}, + {file = "coverage-7.6.9-cp313-cp313t-win32.whl", hash = "sha256:5e6b86b5847a016d0fbd31ffe1001b63355ed309651851295315031ea7eb5a9b"}, + {file = "coverage-7.6.9-cp313-cp313t-win_amd64.whl", hash = "sha256:97ddc94d46088304772d21b060041c97fc16bdda13c6c7f9d8fcd8d5ae0d8611"}, + {file = "coverage-7.6.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:adb697c0bd35100dc690de83154627fbab1f4f3c0386df266dded865fc50a902"}, + {file = "coverage-7.6.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:be57b6d56e49c2739cdf776839a92330e933dd5e5d929966fbbd380c77f060be"}, + {file = "coverage-7.6.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1592791f8204ae9166de22ba7e6705fa4ebd02936c09436a1bb85aabca3e599"}, + {file = "coverage-7.6.9-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e12ae8cc979cf83d258acb5e1f1cf2f3f83524d1564a49d20b8bec14b637f08"}, + {file = "coverage-7.6.9-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb5555cff66c4d3d6213a296b360f9e1a8e323e74e0426b6c10ed7f4d021e464"}, + {file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:b9389a429e0e5142e69d5bf4a435dd688c14478a19bb901735cdf75e57b13845"}, + {file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:592ac539812e9b46046620341498caf09ca21023c41c893e1eb9dbda00a70cbf"}, + {file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a27801adef24cc30871da98a105f77995e13a25a505a0161911f6aafbd66e678"}, + {file = "coverage-7.6.9-cp39-cp39-win32.whl", hash = "sha256:8e3c3e38930cfb729cb8137d7f055e5a473ddaf1217966aa6238c88bd9fd50e6"}, + {file = "coverage-7.6.9-cp39-cp39-win_amd64.whl", hash = "sha256:e28bf44afa2b187cc9f41749138a64435bf340adfcacb5b2290c070ce99839d4"}, + {file = "coverage-7.6.9-pp39.pp310-none-any.whl", hash = "sha256:f3ca78518bc6bc92828cd11867b121891d75cae4ea9e908d72030609b996db1b"}, + {file = "coverage-7.6.9.tar.gz", hash = "sha256:4a8d8977b0c6ef5aeadcb644da9e69ae0dcfe66ec7f368c89c72e058bd71164d"}, +] + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "cycler" +version = "0.12.1" +description = "Composable style cycles" +optional = false +python-versions = ">=3.8" +files = [ + {file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"}, + {file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"}, +] + +[package.extras] +docs = ["ipython", "matplotlib", "numpydoc", "sphinx"] +tests = ["pytest", "pytest-cov", "pytest-xdist"] + +[[package]] +name = "distlib" +version = "0.3.9" +description = "Distribution utilities" +optional = false +python-versions = "*" +files = [ + {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, + {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, +] + +[[package]] +name = "docutils" +version = "0.21.2" +description = "Docutils -- Python Documentation Utilities" +optional = false +python-versions = ">=3.9" +files = [ + {file = "docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"}, + {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, +] + +[[package]] +name = "filelock" +version = "3.16.1" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.8" +files = [ + {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, + {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, +] + +[package.extras] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] +typing = ["typing-extensions (>=4.12.2)"] + +[[package]] +name = "fonttools" +version = "4.55.3" +description = "Tools to manipulate font files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fonttools-4.55.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1dcc07934a2165ccdc3a5a608db56fb3c24b609658a5b340aee4ecf3ba679dc0"}, + {file = "fonttools-4.55.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f7d66c15ba875432a2d2fb419523f5d3d347f91f48f57b8b08a2dfc3c39b8a3f"}, + {file = "fonttools-4.55.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27e4ae3592e62eba83cd2c4ccd9462dcfa603ff78e09110680a5444c6925d841"}, + {file = "fonttools-4.55.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62d65a3022c35e404d19ca14f291c89cc5890032ff04f6c17af0bd1927299674"}, + {file = "fonttools-4.55.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d342e88764fb201286d185093781bf6628bbe380a913c24adf772d901baa8276"}, + {file = "fonttools-4.55.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:dd68c87a2bfe37c5b33bcda0fba39b65a353876d3b9006fde3adae31f97b3ef5"}, + {file = "fonttools-4.55.3-cp310-cp310-win32.whl", hash = "sha256:1bc7ad24ff98846282eef1cbeac05d013c2154f977a79886bb943015d2b1b261"}, + {file = "fonttools-4.55.3-cp310-cp310-win_amd64.whl", hash = "sha256:b54baf65c52952db65df39fcd4820668d0ef4766c0ccdf32879b77f7c804d5c5"}, + {file = "fonttools-4.55.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8c4491699bad88efe95772543cd49870cf756b019ad56294f6498982408ab03e"}, + {file = "fonttools-4.55.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5323a22eabddf4b24f66d26894f1229261021dacd9d29e89f7872dd8c63f0b8b"}, + {file = "fonttools-4.55.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5480673f599ad410695ca2ddef2dfefe9df779a9a5cda89503881e503c9c7d90"}, + {file = "fonttools-4.55.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da9da6d65cd7aa6b0f806556f4985bcbf603bf0c5c590e61b43aa3e5a0f822d0"}, + {file = "fonttools-4.55.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e894b5bd60d9f473bed7a8f506515549cc194de08064d829464088d23097331b"}, + {file = "fonttools-4.55.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:aee3b57643827e237ff6ec6d28d9ff9766bd8b21e08cd13bff479e13d4b14765"}, + {file = "fonttools-4.55.3-cp311-cp311-win32.whl", hash = "sha256:eb6ca911c4c17eb51853143624d8dc87cdcdf12a711fc38bf5bd21521e79715f"}, + {file = "fonttools-4.55.3-cp311-cp311-win_amd64.whl", hash = "sha256:6314bf82c54c53c71805318fcf6786d986461622dd926d92a465199ff54b1b72"}, + {file = "fonttools-4.55.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f9e736f60f4911061235603a6119e72053073a12c6d7904011df2d8fad2c0e35"}, + {file = "fonttools-4.55.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7a8aa2c5e5b8b3bcb2e4538d929f6589a5c6bdb84fd16e2ed92649fb5454f11c"}, + {file = "fonttools-4.55.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07f8288aacf0a38d174445fc78377a97fb0b83cfe352a90c9d9c1400571963c7"}, + {file = "fonttools-4.55.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8d5e8916c0970fbc0f6f1bece0063363bb5857a7f170121a4493e31c3db3314"}, + {file = "fonttools-4.55.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ae3b6600565b2d80b7c05acb8e24d2b26ac407b27a3f2e078229721ba5698427"}, + {file = "fonttools-4.55.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:54153c49913f45065c8d9e6d0c101396725c5621c8aee744719300f79771d75a"}, + {file = "fonttools-4.55.3-cp312-cp312-win32.whl", hash = "sha256:827e95fdbbd3e51f8b459af5ea10ecb4e30af50221ca103bea68218e9615de07"}, + {file = "fonttools-4.55.3-cp312-cp312-win_amd64.whl", hash = "sha256:e6e8766eeeb2de759e862004aa11a9ea3d6f6d5ec710551a88b476192b64fd54"}, + {file = "fonttools-4.55.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a430178ad3e650e695167cb53242dae3477b35c95bef6525b074d87493c4bf29"}, + {file = "fonttools-4.55.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:529cef2ce91dc44f8e407cc567fae6e49a1786f2fefefa73a294704c415322a4"}, + {file = "fonttools-4.55.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e75f12c82127486fac2d8bfbf5bf058202f54bf4f158d367e41647b972342ca"}, + {file = "fonttools-4.55.3-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:859c358ebf41db18fb72342d3080bce67c02b39e86b9fbcf1610cca14984841b"}, + {file = "fonttools-4.55.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:546565028e244a701f73df6d8dd6be489d01617863ec0c6a42fa25bf45d43048"}, + {file = "fonttools-4.55.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:aca318b77f23523309eec4475d1fbbb00a6b133eb766a8bdc401faba91261abe"}, + {file = "fonttools-4.55.3-cp313-cp313-win32.whl", hash = "sha256:8c5ec45428edaa7022f1c949a632a6f298edc7b481312fc7dc258921e9399628"}, + {file = "fonttools-4.55.3-cp313-cp313-win_amd64.whl", hash = "sha256:11e5de1ee0d95af4ae23c1a138b184b7f06e0b6abacabf1d0db41c90b03d834b"}, + {file = "fonttools-4.55.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:caf8230f3e10f8f5d7593eb6d252a37caf58c480b19a17e250a63dad63834cf3"}, + {file = "fonttools-4.55.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b586ab5b15b6097f2fb71cafa3c98edfd0dba1ad8027229e7b1e204a58b0e09d"}, + {file = "fonttools-4.55.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8c2794ded89399cc2169c4d0bf7941247b8d5932b2659e09834adfbb01589aa"}, + {file = "fonttools-4.55.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf4fe7c124aa3f4e4c1940880156e13f2f4d98170d35c749e6b4f119a872551e"}, + {file = "fonttools-4.55.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:86721fbc389ef5cc1e2f477019e5069e8e4421e8d9576e9c26f840dbb04678de"}, + {file = "fonttools-4.55.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:89bdc5d88bdeec1b15af790810e267e8332d92561dce4f0748c2b95c9bdf3926"}, + {file = "fonttools-4.55.3-cp38-cp38-win32.whl", hash = "sha256:bc5dbb4685e51235ef487e4bd501ddfc49be5aede5e40f4cefcccabc6e60fb4b"}, + {file = "fonttools-4.55.3-cp38-cp38-win_amd64.whl", hash = "sha256:cd70de1a52a8ee2d1877b6293af8a2484ac82514f10b1c67c1c5762d38073e56"}, + {file = "fonttools-4.55.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bdcc9f04b36c6c20978d3f060e5323a43f6222accc4e7fcbef3f428e216d96af"}, + {file = "fonttools-4.55.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c3ca99e0d460eff46e033cd3992a969658c3169ffcd533e0a39c63a38beb6831"}, + {file = "fonttools-4.55.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22f38464daa6cdb7b6aebd14ab06609328fe1e9705bb0fcc7d1e69de7109ee02"}, + {file = "fonttools-4.55.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed63959d00b61959b035c7d47f9313c2c1ece090ff63afea702fe86de00dbed4"}, + {file = "fonttools-4.55.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5e8d657cd7326eeaba27de2740e847c6b39dde2f8d7cd7cc56f6aad404ddf0bd"}, + {file = "fonttools-4.55.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:fb594b5a99943042c702c550d5494bdd7577f6ef19b0bc73877c948a63184a32"}, + {file = "fonttools-4.55.3-cp39-cp39-win32.whl", hash = "sha256:dc5294a3d5c84226e3dbba1b6f61d7ad813a8c0238fceea4e09aa04848c3d851"}, + {file = "fonttools-4.55.3-cp39-cp39-win_amd64.whl", hash = "sha256:aedbeb1db64496d098e6be92b2e63b5fac4e53b1b92032dfc6988e1ea9134a4d"}, + {file = "fonttools-4.55.3-py3-none-any.whl", hash = "sha256:f412604ccbeee81b091b420272841e5ec5ef68967a9790e80bffd0e30b8e2977"}, + {file = "fonttools-4.55.3.tar.gz", hash = "sha256:3983313c2a04d6cc1fe9251f8fc647754cf49a61dac6cb1e7249ae67afaafc45"}, +] + +[package.extras] +all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "pycairo", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0)", "xattr", "zopfli (>=0.1.4)"] +graphite = ["lz4 (>=1.7.4.2)"] +interpolatable = ["munkres", "pycairo", "scipy"] +lxml = ["lxml (>=4.0)"] +pathops = ["skia-pathops (>=0.5.0)"] +plot = ["matplotlib"] +repacker = ["uharfbuzz (>=0.23.0)"] +symfont = ["sympy"] +type1 = ["xattr"] +ufo = ["fs (>=2.2.0,<3)"] +unicode = ["unicodedata2 (>=15.1.0)"] +woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] + +[[package]] +name = "identify" +version = "2.6.3" +description = "File identification library for Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "identify-2.6.3-py2.py3-none-any.whl", hash = "sha256:9edba65473324c2ea9684b1f944fe3191db3345e50b6d04571d10ed164f8d7bd"}, + {file = "identify-2.6.3.tar.gz", hash = "sha256:62f5dae9b5fef52c84cc188514e9ea4f3f636b1d8799ab5ebc475471f9e47a02"}, +] + +[package.extras] +license = ["ukkonen"] + +[[package]] +name = "idna" +version = "3.10" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.6" +files = [ + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "imagesize" +version = "1.4.1" +description = "Getting image size from png/jpeg/jpeg2000/gif file" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, + {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "jinja2" +version = "3.1.4" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "kiwisolver" +version = "1.4.7" +description = "A fast implementation of the Cassowary constraint solver" +optional = false +python-versions = ">=3.8" +files = [ + {file = "kiwisolver-1.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8a9c83f75223d5e48b0bc9cb1bf2776cf01563e00ade8775ffe13b0b6e1af3a6"}, + {file = "kiwisolver-1.4.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:58370b1ffbd35407444d57057b57da5d6549d2d854fa30249771775c63b5fe17"}, + {file = "kiwisolver-1.4.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aa0abdf853e09aff551db11fce173e2177d00786c688203f52c87ad7fcd91ef9"}, + {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8d53103597a252fb3ab8b5845af04c7a26d5e7ea8122303dd7a021176a87e8b9"}, + {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:88f17c5ffa8e9462fb79f62746428dd57b46eb931698e42e990ad63103f35e6c"}, + {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a9ca9c710d598fd75ee5de59d5bda2684d9db36a9f50b6125eaea3969c2599"}, + {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f4d742cb7af1c28303a51b7a27aaee540e71bb8e24f68c736f6f2ffc82f2bf05"}, + {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e28c7fea2196bf4c2f8d46a0415c77a1c480cc0724722f23d7410ffe9842c407"}, + {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e968b84db54f9d42046cf154e02911e39c0435c9801681e3fc9ce8a3c4130278"}, + {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0c18ec74c0472de033e1bebb2911c3c310eef5649133dd0bedf2a169a1b269e5"}, + {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8f0ea6da6d393d8b2e187e6a5e3fb81f5862010a40c3945e2c6d12ae45cfb2ad"}, + {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:f106407dda69ae456dd1227966bf445b157ccc80ba0dff3802bb63f30b74e895"}, + {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:84ec80df401cfee1457063732d90022f93951944b5b58975d34ab56bb150dfb3"}, + {file = "kiwisolver-1.4.7-cp310-cp310-win32.whl", hash = "sha256:71bb308552200fb2c195e35ef05de12f0c878c07fc91c270eb3d6e41698c3bcc"}, + {file = "kiwisolver-1.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:44756f9fd339de0fb6ee4f8c1696cfd19b2422e0d70b4cefc1cc7f1f64045a8c"}, + {file = "kiwisolver-1.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:78a42513018c41c2ffd262eb676442315cbfe3c44eed82385c2ed043bc63210a"}, + {file = "kiwisolver-1.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d2b0e12a42fb4e72d509fc994713d099cbb15ebf1103545e8a45f14da2dfca54"}, + {file = "kiwisolver-1.4.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2a8781ac3edc42ea4b90bc23e7d37b665d89423818e26eb6df90698aa2287c95"}, + {file = "kiwisolver-1.4.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:46707a10836894b559e04b0fd143e343945c97fd170d69a2d26d640b4e297935"}, + {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef97b8df011141c9b0f6caf23b29379f87dd13183c978a30a3c546d2c47314cb"}, + {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ab58c12a2cd0fc769089e6d38466c46d7f76aced0a1f54c77652446733d2d02"}, + {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:803b8e1459341c1bb56d1c5c010406d5edec8a0713a0945851290a7930679b51"}, + {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9a9e8a507420fe35992ee9ecb302dab68550dedc0da9e2880dd88071c5fb052"}, + {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18077b53dc3bb490e330669a99920c5e6a496889ae8c63b58fbc57c3d7f33a18"}, + {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6af936f79086a89b3680a280c47ea90b4df7047b5bdf3aa5c524bbedddb9e545"}, + {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3abc5b19d24af4b77d1598a585b8a719beb8569a71568b66f4ebe1fb0449460b"}, + {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:933d4de052939d90afbe6e9d5273ae05fb836cc86c15b686edd4b3560cc0ee36"}, + {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:65e720d2ab2b53f1f72fb5da5fb477455905ce2c88aaa671ff0a447c2c80e8e3"}, + {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3bf1ed55088f214ba6427484c59553123fdd9b218a42bbc8c6496d6754b1e523"}, + {file = "kiwisolver-1.4.7-cp311-cp311-win32.whl", hash = "sha256:4c00336b9dd5ad96d0a558fd18a8b6f711b7449acce4c157e7343ba92dd0cf3d"}, + {file = "kiwisolver-1.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:929e294c1ac1e9f615c62a4e4313ca1823ba37326c164ec720a803287c4c499b"}, + {file = "kiwisolver-1.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:e33e8fbd440c917106b237ef1a2f1449dfbb9b6f6e1ce17c94cd6a1e0d438376"}, + {file = "kiwisolver-1.4.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:5360cc32706dab3931f738d3079652d20982511f7c0ac5711483e6eab08efff2"}, + {file = "kiwisolver-1.4.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:942216596dc64ddb25adb215c3c783215b23626f8d84e8eff8d6d45c3f29f75a"}, + {file = "kiwisolver-1.4.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:48b571ecd8bae15702e4f22d3ff6a0f13e54d3d00cd25216d5e7f658242065ee"}, + {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad42ba922c67c5f219097b28fae965e10045ddf145d2928bfac2eb2e17673640"}, + {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:612a10bdae23404a72941a0fc8fa2660c6ea1217c4ce0dbcab8a8f6543ea9e7f"}, + {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9e838bba3a3bac0fe06d849d29772eb1afb9745a59710762e4ba3f4cb8424483"}, + {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:22f499f6157236c19f4bbbd472fa55b063db77a16cd74d49afe28992dff8c258"}, + {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693902d433cf585133699972b6d7c42a8b9f8f826ebcaf0132ff55200afc599e"}, + {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4e77f2126c3e0b0d055f44513ed349038ac180371ed9b52fe96a32aa071a5107"}, + {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:657a05857bda581c3656bfc3b20e353c232e9193eb167766ad2dc58b56504948"}, + {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4bfa75a048c056a411f9705856abfc872558e33c055d80af6a380e3658766038"}, + {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:34ea1de54beef1c104422d210c47c7d2a4999bdecf42c7b5718fbe59a4cac383"}, + {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:90da3b5f694b85231cf93586dad5e90e2d71b9428f9aad96952c99055582f520"}, + {file = "kiwisolver-1.4.7-cp312-cp312-win32.whl", hash = "sha256:18e0cca3e008e17fe9b164b55735a325140a5a35faad8de92dd80265cd5eb80b"}, + {file = "kiwisolver-1.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:58cb20602b18f86f83a5c87d3ee1c766a79c0d452f8def86d925e6c60fbf7bfb"}, + {file = "kiwisolver-1.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:f5a8b53bdc0b3961f8b6125e198617c40aeed638b387913bf1ce78afb1b0be2a"}, + {file = "kiwisolver-1.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2e6039dcbe79a8e0f044f1c39db1986a1b8071051efba3ee4d74f5b365f5226e"}, + {file = "kiwisolver-1.4.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a1ecf0ac1c518487d9d23b1cd7139a6a65bc460cd101ab01f1be82ecf09794b6"}, + {file = "kiwisolver-1.4.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7ab9ccab2b5bd5702ab0803676a580fffa2aa178c2badc5557a84cc943fcf750"}, + {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f816dd2277f8d63d79f9c8473a79fe54047bc0467754962840782c575522224d"}, + {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf8bcc23ceb5a1b624572a1623b9f79d2c3b337c8c455405ef231933a10da379"}, + {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dea0bf229319828467d7fca8c7c189780aa9ff679c94539eed7532ebe33ed37c"}, + {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c06a4c7cf15ec739ce0e5971b26c93638730090add60e183530d70848ebdd34"}, + {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:913983ad2deb14e66d83c28b632fd35ba2b825031f2fa4ca29675e665dfecbe1"}, + {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5337ec7809bcd0f424c6b705ecf97941c46279cf5ed92311782c7c9c2026f07f"}, + {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4c26ed10c4f6fa6ddb329a5120ba3b6db349ca192ae211e882970bfc9d91420b"}, + {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c619b101e6de2222c1fcb0531e1b17bbffbe54294bfba43ea0d411d428618c27"}, + {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:073a36c8273647592ea332e816e75ef8da5c303236ec0167196793eb1e34657a"}, + {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3ce6b2b0231bda412463e152fc18335ba32faf4e8c23a754ad50ffa70e4091ee"}, + {file = "kiwisolver-1.4.7-cp313-cp313-win32.whl", hash = "sha256:f4c9aee212bc89d4e13f58be11a56cc8036cabad119259d12ace14b34476fd07"}, + {file = "kiwisolver-1.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:8a3ec5aa8e38fc4c8af308917ce12c536f1c88452ce554027e55b22cbbfbff76"}, + {file = "kiwisolver-1.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:76c8094ac20ec259471ac53e774623eb62e6e1f56cd8690c67ce6ce4fcb05650"}, + {file = "kiwisolver-1.4.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5d5abf8f8ec1f4e22882273c423e16cae834c36856cac348cfbfa68e01c40f3a"}, + {file = "kiwisolver-1.4.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:aeb3531b196ef6f11776c21674dba836aeea9d5bd1cf630f869e3d90b16cfade"}, + {file = "kiwisolver-1.4.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b7d755065e4e866a8086c9bdada157133ff466476a2ad7861828e17b6026e22c"}, + {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08471d4d86cbaec61f86b217dd938a83d85e03785f51121e791a6e6689a3be95"}, + {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7bbfcb7165ce3d54a3dfbe731e470f65739c4c1f85bb1018ee912bae139e263b"}, + {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d34eb8494bea691a1a450141ebb5385e4b69d38bb8403b5146ad279f4b30fa3"}, + {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9242795d174daa40105c1d86aba618e8eab7bf96ba8c3ee614da8302a9f95503"}, + {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a0f64a48bb81af7450e641e3fe0b0394d7381e342805479178b3d335d60ca7cf"}, + {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8e045731a5416357638d1700927529e2b8ab304811671f665b225f8bf8d8f933"}, + {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:4322872d5772cae7369f8351da1edf255a604ea7087fe295411397d0cfd9655e"}, + {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:e1631290ee9271dffe3062d2634c3ecac02c83890ada077d225e081aca8aab89"}, + {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:edcfc407e4eb17e037bca59be0e85a2031a2ac87e4fed26d3e9df88b4165f92d"}, + {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4d05d81ecb47d11e7f8932bd8b61b720bf0b41199358f3f5e36d38e28f0532c5"}, + {file = "kiwisolver-1.4.7-cp38-cp38-win32.whl", hash = "sha256:b38ac83d5f04b15e515fd86f312479d950d05ce2368d5413d46c088dda7de90a"}, + {file = "kiwisolver-1.4.7-cp38-cp38-win_amd64.whl", hash = "sha256:d83db7cde68459fc803052a55ace60bea2bae361fc3b7a6d5da07e11954e4b09"}, + {file = "kiwisolver-1.4.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3f9362ecfca44c863569d3d3c033dbe8ba452ff8eed6f6b5806382741a1334bd"}, + {file = "kiwisolver-1.4.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e8df2eb9b2bac43ef8b082e06f750350fbbaf2887534a5be97f6cf07b19d9583"}, + {file = "kiwisolver-1.4.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f32d6edbc638cde7652bd690c3e728b25332acbadd7cad670cc4a02558d9c417"}, + {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e2e6c39bd7b9372b0be21456caab138e8e69cc0fc1190a9dfa92bd45a1e6e904"}, + {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dda56c24d869b1193fcc763f1284b9126550eaf84b88bbc7256e15028f19188a"}, + {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79849239c39b5e1fd906556c474d9b0439ea6792b637511f3fe3a41158d89ca8"}, + {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5e3bc157fed2a4c02ec468de4ecd12a6e22818d4f09cde2c31ee3226ffbefab2"}, + {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3da53da805b71e41053dc670f9a820d1157aae77b6b944e08024d17bcd51ef88"}, + {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8705f17dfeb43139a692298cb6637ee2e59c0194538153e83e9ee0c75c2eddde"}, + {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:82a5c2f4b87c26bb1a0ef3d16b5c4753434633b83d365cc0ddf2770c93829e3c"}, + {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce8be0466f4c0d585cdb6c1e2ed07232221df101a4c6f28821d2aa754ca2d9e2"}, + {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:409afdfe1e2e90e6ee7fc896f3df9a7fec8e793e58bfa0d052c8a82f99c37abb"}, + {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5b9c3f4ee0b9a439d2415012bd1b1cc2df59e4d6a9939f4d669241d30b414327"}, + {file = "kiwisolver-1.4.7-cp39-cp39-win32.whl", hash = "sha256:a79ae34384df2b615eefca647a2873842ac3b596418032bef9a7283675962644"}, + {file = "kiwisolver-1.4.7-cp39-cp39-win_amd64.whl", hash = "sha256:cf0438b42121a66a3a667de17e779330fc0f20b0d97d59d2f2121e182b0505e4"}, + {file = "kiwisolver-1.4.7-cp39-cp39-win_arm64.whl", hash = "sha256:764202cc7e70f767dab49e8df52c7455e8de0df5d858fa801a11aa0d882ccf3f"}, + {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:94252291e3fe68001b1dd747b4c0b3be12582839b95ad4d1b641924d68fd4643"}, + {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5b7dfa3b546da08a9f622bb6becdb14b3e24aaa30adba66749d38f3cc7ea9706"}, + {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd3de6481f4ed8b734da5df134cd5a6a64fe32124fe83dde1e5b5f29fe30b1e6"}, + {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a91b5f9f1205845d488c928e8570dcb62b893372f63b8b6e98b863ebd2368ff2"}, + {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40fa14dbd66b8b8f470d5fc79c089a66185619d31645f9b0773b88b19f7223c4"}, + {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:eb542fe7933aa09d8d8f9d9097ef37532a7df6497819d16efe4359890a2f417a"}, + {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bfa1acfa0c54932d5607e19a2c24646fb4c1ae2694437789129cf099789a3b00"}, + {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:eee3ea935c3d227d49b4eb85660ff631556841f6e567f0f7bda972df6c2c9935"}, + {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f3160309af4396e0ed04db259c3ccbfdc3621b5559b5453075e5de555e1f3a1b"}, + {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a17f6a29cf8935e587cc8a4dbfc8368c55edc645283db0ce9801016f83526c2d"}, + {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:10849fb2c1ecbfae45a693c070e0320a91b35dd4bcf58172c023b994283a124d"}, + {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:ac542bf38a8a4be2dc6b15248d36315ccc65f0743f7b1a76688ffb6b5129a5c2"}, + {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:8b01aac285f91ca889c800042c35ad3b239e704b150cfd3382adfc9dcc780e39"}, + {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:48be928f59a1f5c8207154f935334d374e79f2b5d212826307d072595ad76a2e"}, + {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f37cfe618a117e50d8c240555331160d73d0411422b59b5ee217843d7b693608"}, + {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:599b5c873c63a1f6ed7eead644a8a380cfbdf5db91dcb6f85707aaab213b1674"}, + {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:801fa7802e5cfabe3ab0c81a34c323a319b097dfb5004be950482d882f3d7225"}, + {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:0c6c43471bc764fad4bc99c5c2d6d16a676b1abf844ca7c8702bdae92df01ee0"}, + {file = "kiwisolver-1.4.7.tar.gz", hash = "sha256:9893ff81bd7107f7b685d3017cc6583daadb4fc26e4a888350df530e41980a60"}, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.9" +files = [ + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, + {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, +] + +[[package]] +name = "matplotlib" +version = "3.10.0" +description = "Python plotting package" +optional = false +python-versions = ">=3.10" +files = [ + {file = "matplotlib-3.10.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2c5829a5a1dd5a71f0e31e6e8bb449bc0ee9dbfb05ad28fc0c6b55101b3a4be6"}, + {file = "matplotlib-3.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a2a43cbefe22d653ab34bb55d42384ed30f611bcbdea1f8d7f431011a2e1c62e"}, + {file = "matplotlib-3.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:607b16c8a73943df110f99ee2e940b8a1cbf9714b65307c040d422558397dac5"}, + {file = "matplotlib-3.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01d2b19f13aeec2e759414d3bfe19ddfb16b13a1250add08d46d5ff6f9be83c6"}, + {file = "matplotlib-3.10.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e6c6461e1fc63df30bf6f80f0b93f5b6784299f721bc28530477acd51bfc3d1"}, + {file = "matplotlib-3.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:994c07b9d9fe8d25951e3202a68c17900679274dadfc1248738dcfa1bd40d7f3"}, + {file = "matplotlib-3.10.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:fd44fc75522f58612ec4a33958a7e5552562b7705b42ef1b4f8c0818e304a363"}, + {file = "matplotlib-3.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c58a9622d5dbeb668f407f35f4e6bfac34bb9ecdcc81680c04d0258169747997"}, + {file = "matplotlib-3.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:845d96568ec873be63f25fa80e9e7fae4be854a66a7e2f0c8ccc99e94a8bd4ef"}, + {file = "matplotlib-3.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5439f4c5a3e2e8eab18e2f8c3ef929772fd5641876db71f08127eed95ab64683"}, + {file = "matplotlib-3.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4673ff67a36152c48ddeaf1135e74ce0d4bce1bbf836ae40ed39c29edf7e2765"}, + {file = "matplotlib-3.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:7e8632baebb058555ac0cde75db885c61f1212e47723d63921879806b40bec6a"}, + {file = "matplotlib-3.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4659665bc7c9b58f8c00317c3c2a299f7f258eeae5a5d56b4c64226fca2f7c59"}, + {file = "matplotlib-3.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d44cb942af1693cced2604c33a9abcef6205601c445f6d0dc531d813af8a2f5a"}, + {file = "matplotlib-3.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a994f29e968ca002b50982b27168addfd65f0105610b6be7fa515ca4b5307c95"}, + {file = "matplotlib-3.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b0558bae37f154fffda54d779a592bc97ca8b4701f1c710055b609a3bac44c8"}, + {file = "matplotlib-3.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:503feb23bd8c8acc75541548a1d709c059b7184cde26314896e10a9f14df5f12"}, + {file = "matplotlib-3.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:c40ba2eb08b3f5de88152c2333c58cee7edcead0a2a0d60fcafa116b17117adc"}, + {file = "matplotlib-3.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96f2886f5c1e466f21cc41b70c5a0cd47bfa0015eb2d5793c88ebce658600e25"}, + {file = "matplotlib-3.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:12eaf48463b472c3c0f8dbacdbf906e573013df81a0ab82f0616ea4b11281908"}, + {file = "matplotlib-3.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fbbabc82fde51391c4da5006f965e36d86d95f6ee83fb594b279564a4c5d0d2"}, + {file = "matplotlib-3.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad2e15300530c1a94c63cfa546e3b7864bd18ea2901317bae8bbf06a5ade6dcf"}, + {file = "matplotlib-3.10.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3547d153d70233a8496859097ef0312212e2689cdf8d7ed764441c77604095ae"}, + {file = "matplotlib-3.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:c55b20591ced744aa04e8c3e4b7543ea4d650b6c3c4b208c08a05b4010e8b442"}, + {file = "matplotlib-3.10.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:9ade1003376731a971e398cc4ef38bb83ee8caf0aee46ac6daa4b0506db1fd06"}, + {file = "matplotlib-3.10.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:95b710fea129c76d30be72c3b38f330269363fbc6e570a5dd43580487380b5ff"}, + {file = "matplotlib-3.10.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cdbaf909887373c3e094b0318d7ff230b2ad9dcb64da7ade654182872ab2593"}, + {file = "matplotlib-3.10.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d907fddb39f923d011875452ff1eca29a9e7f21722b873e90db32e5d8ddff12e"}, + {file = "matplotlib-3.10.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3b427392354d10975c1d0f4ee18aa5844640b512d5311ef32efd4dd7db106ede"}, + {file = "matplotlib-3.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5fd41b0ec7ee45cd960a8e71aea7c946a28a0b8a4dcee47d2856b2af051f334c"}, + {file = "matplotlib-3.10.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:81713dd0d103b379de4516b861d964b1d789a144103277769238c732229d7f03"}, + {file = "matplotlib-3.10.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:359f87baedb1f836ce307f0e850d12bb5f1936f70d035561f90d41d305fdacea"}, + {file = "matplotlib-3.10.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae80dc3a4add4665cf2faa90138384a7ffe2a4e37c58d83e115b54287c4f06ef"}, + {file = "matplotlib-3.10.0.tar.gz", hash = "sha256:b886d02a581b96704c9d1ffe55709e49b4d2d52709ccebc4be42db856e511278"}, +] + +[package.dependencies] +contourpy = ">=1.0.1" +cycler = ">=0.10" +fonttools = ">=4.22.0" +kiwisolver = ">=1.3.1" +numpy = ">=1.23" +packaging = ">=20.0" +pillow = ">=8" +pyparsing = ">=2.3.1" +python-dateutil = ">=2.7" + +[package.extras] +dev = ["meson-python (>=0.13.1,<0.17.0)", "pybind11 (>=2.13.2,!=2.13.3)", "setuptools (>=64)", "setuptools_scm (>=7)"] + +[[package]] +name = "mpmath" +version = "1.3.0" +description = "Python library for arbitrary-precision floating-point arithmetic" +optional = false +python-versions = "*" +files = [ + {file = "mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c"}, + {file = "mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f"}, +] + +[package.extras] +develop = ["codecov", "pycodestyle", "pytest (>=4.6)", "pytest-cov", "wheel"] +docs = ["sphinx"] +gmpy = ["gmpy2 (>=2.1.0a4)"] +tests = ["pytest (>=4.6)"] + +[[package]] +name = "nodeenv" +version = "1.9.1" +description = "Node.js virtual environment builder" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, + {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, +] + +[[package]] +name = "numpy" +version = "2.2.0" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.10" +files = [ + {file = "numpy-2.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1e25507d85da11ff5066269d0bd25d06e0a0f2e908415534f3e603d2a78e4ffa"}, + {file = "numpy-2.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a62eb442011776e4036af5c8b1a00b706c5bc02dc15eb5344b0c750428c94219"}, + {file = "numpy-2.2.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:b606b1aaf802e6468c2608c65ff7ece53eae1a6874b3765f69b8ceb20c5fa78e"}, + {file = "numpy-2.2.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:36b2b43146f646642b425dd2027730f99bac962618ec2052932157e213a040e9"}, + {file = "numpy-2.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fe8f3583e0607ad4e43a954e35c1748b553bfe9fdac8635c02058023277d1b3"}, + {file = "numpy-2.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:122fd2fcfafdefc889c64ad99c228d5a1f9692c3a83f56c292618a59aa60ae83"}, + {file = "numpy-2.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3f2f5cddeaa4424a0a118924b988746db6ffa8565e5829b1841a8a3bd73eb59a"}, + {file = "numpy-2.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7fe4bb0695fe986a9e4deec3b6857003b4cfe5c5e4aac0b95f6a658c14635e31"}, + {file = "numpy-2.2.0-cp310-cp310-win32.whl", hash = "sha256:b30042fe92dbd79f1ba7f6898fada10bdaad1847c44f2dff9a16147e00a93661"}, + {file = "numpy-2.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:54dc1d6d66f8d37843ed281773c7174f03bf7ad826523f73435deb88ba60d2d4"}, + {file = "numpy-2.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9874bc2ff574c40ab7a5cbb7464bf9b045d617e36754a7bc93f933d52bd9ffc6"}, + {file = "numpy-2.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0da8495970f6b101ddd0c38ace92edea30e7e12b9a926b57f5fabb1ecc25bb90"}, + {file = "numpy-2.2.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0557eebc699c1c34cccdd8c3778c9294e8196df27d713706895edc6f57d29608"}, + {file = "numpy-2.2.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:3579eaeb5e07f3ded59298ce22b65f877a86ba8e9fe701f5576c99bb17c283da"}, + {file = "numpy-2.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40deb10198bbaa531509aad0cd2f9fadb26c8b94070831e2208e7df543562b74"}, + {file = "numpy-2.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2aed8fcf8abc3020d6a9ccb31dbc9e7d7819c56a348cc88fd44be269b37427e"}, + {file = "numpy-2.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a222d764352c773aa5ebde02dd84dba3279c81c6db2e482d62a3fa54e5ece69b"}, + {file = "numpy-2.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4e58666988605e251d42c2818c7d3d8991555381be26399303053b58a5bbf30d"}, + {file = "numpy-2.2.0-cp311-cp311-win32.whl", hash = "sha256:4723a50e1523e1de4fccd1b9a6dcea750c2102461e9a02b2ac55ffeae09a4410"}, + {file = "numpy-2.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:16757cf28621e43e252c560d25b15f18a2f11da94fea344bf26c599b9cf54b73"}, + {file = "numpy-2.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cff210198bb4cae3f3c100444c5eaa573a823f05c253e7188e1362a5555235b3"}, + {file = "numpy-2.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58b92a5828bd4d9aa0952492b7de803135038de47343b2aa3cc23f3b71a3dc4e"}, + {file = "numpy-2.2.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:ebe5e59545401fbb1b24da76f006ab19734ae71e703cdb4a8b347e84a0cece67"}, + {file = "numpy-2.2.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e2b8cd48a9942ed3f85b95ca4105c45758438c7ed28fff1e4ce3e57c3b589d8e"}, + {file = "numpy-2.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57fcc997ffc0bef234b8875a54d4058afa92b0b0c4223fc1f62f24b3b5e86038"}, + {file = "numpy-2.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ad7d11b309bd132d74397fcf2920933c9d1dc865487128f5c03d580f2c3d03"}, + {file = "numpy-2.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cb24cca1968b21355cc6f3da1a20cd1cebd8a023e3c5b09b432444617949085a"}, + {file = "numpy-2.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0798b138c291d792f8ea40fe3768610f3c7dd2574389e37c3f26573757c8f7ef"}, + {file = "numpy-2.2.0-cp312-cp312-win32.whl", hash = "sha256:afe8fb968743d40435c3827632fd36c5fbde633b0423da7692e426529b1759b1"}, + {file = "numpy-2.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:3a4199f519e57d517ebd48cb76b36c82da0360781c6a0353e64c0cac30ecaad3"}, + {file = "numpy-2.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f8c8b141ef9699ae777c6278b52c706b653bf15d135d302754f6b2e90eb30367"}, + {file = "numpy-2.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0f0986e917aca18f7a567b812ef7ca9391288e2acb7a4308aa9d265bd724bdae"}, + {file = "numpy-2.2.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:1c92113619f7b272838b8d6702a7f8ebe5edea0df48166c47929611d0b4dea69"}, + {file = "numpy-2.2.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:5a145e956b374e72ad1dff82779177d4a3c62bc8248f41b80cb5122e68f22d13"}, + {file = "numpy-2.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18142b497d70a34b01642b9feabb70156311b326fdddd875a9981f34a369b671"}, + {file = "numpy-2.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7d41d1612c1a82b64697e894b75db6758d4f21c3ec069d841e60ebe54b5b571"}, + {file = "numpy-2.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a98f6f20465e7618c83252c02041517bd2f7ea29be5378f09667a8f654a5918d"}, + {file = "numpy-2.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e09d40edfdb4e260cb1567d8ae770ccf3b8b7e9f0d9b5c2a9992696b30ce2742"}, + {file = "numpy-2.2.0-cp313-cp313-win32.whl", hash = "sha256:3905a5fffcc23e597ee4d9fb3fcd209bd658c352657548db7316e810ca80458e"}, + {file = "numpy-2.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:a184288538e6ad699cbe6b24859206e38ce5fba28f3bcfa51c90d0502c1582b2"}, + {file = "numpy-2.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7832f9e8eb00be32f15fdfb9a981d6955ea9adc8574c521d48710171b6c55e95"}, + {file = "numpy-2.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f0dd071b95bbca244f4cb7f70b77d2ff3aaaba7fa16dc41f58d14854a6204e6c"}, + {file = "numpy-2.2.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:b0b227dcff8cdc3efbce66d4e50891f04d0a387cce282fe1e66199146a6a8fca"}, + {file = "numpy-2.2.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:6ab153263a7c5ccaf6dfe7e53447b74f77789f28ecb278c3b5d49db7ece10d6d"}, + {file = "numpy-2.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e500aba968a48e9019e42c0c199b7ec0696a97fa69037bea163b55398e390529"}, + {file = "numpy-2.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:440cfb3db4c5029775803794f8638fbdbf71ec702caf32735f53b008e1eaece3"}, + {file = "numpy-2.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a55dc7a7f0b6198b07ec0cd445fbb98b05234e8b00c5ac4874a63372ba98d4ab"}, + {file = "numpy-2.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4bddbaa30d78c86329b26bd6aaaea06b1e47444da99eddac7bf1e2fab717bd72"}, + {file = "numpy-2.2.0-cp313-cp313t-win32.whl", hash = "sha256:30bf971c12e4365153afb31fc73f441d4da157153f3400b82db32d04de1e4066"}, + {file = "numpy-2.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d35717333b39d1b6bb8433fa758a55f1081543de527171543a2b710551d40881"}, + {file = "numpy-2.2.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e12c6c1ce84628c52d6367863773f7c8c8241be554e8b79686e91a43f1733773"}, + {file = "numpy-2.2.0-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:b6207dc8fb3c8cb5668e885cef9ec7f70189bec4e276f0ff70d5aa078d32c88e"}, + {file = "numpy-2.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a50aeff71d0f97b6450d33940c7181b08be1441c6c193e678211bff11aa725e7"}, + {file = "numpy-2.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:df12a1f99b99f569a7c2ae59aa2d31724e8d835fc7f33e14f4792e3071d11221"}, + {file = "numpy-2.2.0.tar.gz", hash = "sha256:140dd80ff8981a583a60980be1a655068f8adebf7a45a06a6858c873fcdcd4a0"}, +] + +[[package]] +name = "packaging" +version = "24.2" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, +] + +[[package]] +name = "pgraph-python" +version = "0.6.2" +description = "Mathematical graphs for Python." +optional = false +python-versions = ">=3.6" +files = [ + {file = "pgraph-python-0.6.2.tar.gz", hash = "sha256:4abad7ded90677e4d1fc8317de298c7068942f8b289ec9a58bf3d36d666e70cf"}, + {file = "pgraph_python-0.6.2-py3-none-any.whl", hash = "sha256:0f97585df83c3a2acaaa721970733b11f0fd35319f4a66464066b198ca86fc0c"}, +] + +[package.dependencies] +ansitable = "*" +matplotlib = "*" +numpy = ">=1.18.0" +spatialmath-python = "*" + +[package.extras] +dev = ["pytest", "pytest-cov"] +docs = ["sphinx", "sphinx-autorun", "sphinx-rtd-theme"] + +[[package]] +name = "pillow" +version = "11.0.0" +description = "Python Imaging Library (Fork)" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pillow-11.0.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:6619654954dc4936fcff82db8eb6401d3159ec6be81e33c6000dfd76ae189947"}, + {file = "pillow-11.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b3c5ac4bed7519088103d9450a1107f76308ecf91d6dabc8a33a2fcfb18d0fba"}, + {file = "pillow-11.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a65149d8ada1055029fcb665452b2814fe7d7082fcb0c5bed6db851cb69b2086"}, + {file = "pillow-11.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88a58d8ac0cc0e7f3a014509f0455248a76629ca9b604eca7dc5927cc593c5e9"}, + {file = "pillow-11.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:c26845094b1af3c91852745ae78e3ea47abf3dbcd1cf962f16b9a5fbe3ee8488"}, + {file = "pillow-11.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:1a61b54f87ab5786b8479f81c4b11f4d61702830354520837f8cc791ebba0f5f"}, + {file = "pillow-11.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:674629ff60030d144b7bca2b8330225a9b11c482ed408813924619c6f302fdbb"}, + {file = "pillow-11.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:598b4e238f13276e0008299bd2482003f48158e2b11826862b1eb2ad7c768b97"}, + {file = "pillow-11.0.0-cp310-cp310-win32.whl", hash = "sha256:9a0f748eaa434a41fccf8e1ee7a3eed68af1b690e75328fd7a60af123c193b50"}, + {file = "pillow-11.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:a5629742881bcbc1f42e840af185fd4d83a5edeb96475a575f4da50d6ede337c"}, + {file = "pillow-11.0.0-cp310-cp310-win_arm64.whl", hash = "sha256:ee217c198f2e41f184f3869f3e485557296d505b5195c513b2bfe0062dc537f1"}, + {file = "pillow-11.0.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1c1d72714f429a521d8d2d018badc42414c3077eb187a59579f28e4270b4b0fc"}, + {file = "pillow-11.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:499c3a1b0d6fc8213519e193796eb1a86a1be4b1877d678b30f83fd979811d1a"}, + {file = "pillow-11.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8b2351c85d855293a299038e1f89db92a2f35e8d2f783489c6f0b2b5f3fe8a3"}, + {file = "pillow-11.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f4dba50cfa56f910241eb7f883c20f1e7b1d8f7d91c750cd0b318bad443f4d5"}, + {file = "pillow-11.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:5ddbfd761ee00c12ee1be86c9c0683ecf5bb14c9772ddbd782085779a63dd55b"}, + {file = "pillow-11.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:45c566eb10b8967d71bf1ab8e4a525e5a93519e29ea071459ce517f6b903d7fa"}, + {file = "pillow-11.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b4fd7bd29610a83a8c9b564d457cf5bd92b4e11e79a4ee4716a63c959699b306"}, + {file = "pillow-11.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cb929ca942d0ec4fac404cbf520ee6cac37bf35be479b970c4ffadf2b6a1cad9"}, + {file = "pillow-11.0.0-cp311-cp311-win32.whl", hash = "sha256:006bcdd307cc47ba43e924099a038cbf9591062e6c50e570819743f5607404f5"}, + {file = "pillow-11.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:52a2d8323a465f84faaba5236567d212c3668f2ab53e1c74c15583cf507a0291"}, + {file = "pillow-11.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:16095692a253047fe3ec028e951fa4221a1f3ed3d80c397e83541a3037ff67c9"}, + {file = "pillow-11.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2c0a187a92a1cb5ef2c8ed5412dd8d4334272617f532d4ad4de31e0495bd923"}, + {file = "pillow-11.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:084a07ef0821cfe4858fe86652fffac8e187b6ae677e9906e192aafcc1b69903"}, + {file = "pillow-11.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8069c5179902dcdce0be9bfc8235347fdbac249d23bd90514b7a47a72d9fecf4"}, + {file = "pillow-11.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f02541ef64077f22bf4924f225c0fd1248c168f86e4b7abdedd87d6ebaceab0f"}, + {file = "pillow-11.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:fcb4621042ac4b7865c179bb972ed0da0218a076dc1820ffc48b1d74c1e37fe9"}, + {file = "pillow-11.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:00177a63030d612148e659b55ba99527803288cea7c75fb05766ab7981a8c1b7"}, + {file = "pillow-11.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8853a3bf12afddfdf15f57c4b02d7ded92c7a75a5d7331d19f4f9572a89c17e6"}, + {file = "pillow-11.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3107c66e43bda25359d5ef446f59c497de2b5ed4c7fdba0894f8d6cf3822dafc"}, + {file = "pillow-11.0.0-cp312-cp312-win32.whl", hash = "sha256:86510e3f5eca0ab87429dd77fafc04693195eec7fd6a137c389c3eeb4cfb77c6"}, + {file = "pillow-11.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:8ec4a89295cd6cd4d1058a5e6aec6bf51e0eaaf9714774e1bfac7cfc9051db47"}, + {file = "pillow-11.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:27a7860107500d813fcd203b4ea19b04babe79448268403172782754870dac25"}, + {file = "pillow-11.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bcd1fb5bb7b07f64c15618c89efcc2cfa3e95f0e3bcdbaf4642509de1942a699"}, + {file = "pillow-11.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0e038b0745997c7dcaae350d35859c9715c71e92ffb7e0f4a8e8a16732150f38"}, + {file = "pillow-11.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ae08bd8ffc41aebf578c2af2f9d8749d91f448b3bfd41d7d9ff573d74f2a6b2"}, + {file = "pillow-11.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d69bfd8ec3219ae71bcde1f942b728903cad25fafe3100ba2258b973bd2bc1b2"}, + {file = "pillow-11.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:61b887f9ddba63ddf62fd02a3ba7add935d053b6dd7d58998c630e6dbade8527"}, + {file = "pillow-11.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:c6a660307ca9d4867caa8d9ca2c2658ab685de83792d1876274991adec7b93fa"}, + {file = "pillow-11.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:73e3a0200cdda995c7e43dd47436c1548f87a30bb27fb871f352a22ab8dcf45f"}, + {file = "pillow-11.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fba162b8872d30fea8c52b258a542c5dfd7b235fb5cb352240c8d63b414013eb"}, + {file = "pillow-11.0.0-cp313-cp313-win32.whl", hash = "sha256:f1b82c27e89fffc6da125d5eb0ca6e68017faf5efc078128cfaa42cf5cb38798"}, + {file = "pillow-11.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ba470552b48e5835f1d23ecb936bb7f71d206f9dfeee64245f30c3270b994de"}, + {file = "pillow-11.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:846e193e103b41e984ac921b335df59195356ce3f71dcfd155aa79c603873b84"}, + {file = "pillow-11.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4ad70c4214f67d7466bea6a08061eba35c01b1b89eaa098040a35272a8efb22b"}, + {file = "pillow-11.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6ec0d5af64f2e3d64a165f490d96368bb5dea8b8f9ad04487f9ab60dc4bb6003"}, + {file = "pillow-11.0.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c809a70e43c7977c4a42aefd62f0131823ebf7dd73556fa5d5950f5b354087e2"}, + {file = "pillow-11.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:4b60c9520f7207aaf2e1d94de026682fc227806c6e1f55bba7606d1c94dd623a"}, + {file = "pillow-11.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1e2688958a840c822279fda0086fec1fdab2f95bf2b717b66871c4ad9859d7e8"}, + {file = "pillow-11.0.0-cp313-cp313t-win32.whl", hash = "sha256:607bbe123c74e272e381a8d1957083a9463401f7bd01287f50521ecb05a313f8"}, + {file = "pillow-11.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5c39ed17edea3bc69c743a8dd3e9853b7509625c2462532e62baa0732163a904"}, + {file = "pillow-11.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:75acbbeb05b86bc53cbe7b7e6fe00fbcf82ad7c684b3ad82e3d711da9ba287d3"}, + {file = "pillow-11.0.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2e46773dc9f35a1dd28bd6981332fd7f27bec001a918a72a79b4133cf5291dba"}, + {file = "pillow-11.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2679d2258b7f1192b378e2893a8a0a0ca472234d4c2c0e6bdd3380e8dfa21b6a"}, + {file = "pillow-11.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eda2616eb2313cbb3eebbe51f19362eb434b18e3bb599466a1ffa76a033fb916"}, + {file = "pillow-11.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ec184af98a121fb2da42642dea8a29ec80fc3efbaefb86d8fdd2606619045d"}, + {file = "pillow-11.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:8594f42df584e5b4bb9281799698403f7af489fba84c34d53d1c4bfb71b7c4e7"}, + {file = "pillow-11.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:c12b5ae868897c7338519c03049a806af85b9b8c237b7d675b8c5e089e4a618e"}, + {file = "pillow-11.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:70fbbdacd1d271b77b7721fe3cdd2d537bbbd75d29e6300c672ec6bb38d9672f"}, + {file = "pillow-11.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5178952973e588b3f1360868847334e9e3bf49d19e169bbbdfaf8398002419ae"}, + {file = "pillow-11.0.0-cp39-cp39-win32.whl", hash = "sha256:8c676b587da5673d3c75bd67dd2a8cdfeb282ca38a30f37950511766b26858c4"}, + {file = "pillow-11.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:94f3e1780abb45062287b4614a5bc0874519c86a777d4a7ad34978e86428b8dd"}, + {file = "pillow-11.0.0-cp39-cp39-win_arm64.whl", hash = "sha256:290f2cc809f9da7d6d622550bbf4c1e57518212da51b6a30fe8e0a270a5b78bd"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1187739620f2b365de756ce086fdb3604573337cc28a0d3ac4a01ab6b2d2a6d2"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fbbcb7b57dc9c794843e3d1258c0fbf0f48656d46ffe9e09b63bbd6e8cd5d0a2"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d203af30149ae339ad1b4f710d9844ed8796e97fda23ffbc4cc472968a47d0b"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21a0d3b115009ebb8ac3d2ebec5c2982cc693da935f4ab7bb5c8ebe2f47d36f2"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:73853108f56df97baf2bb8b522f3578221e56f646ba345a372c78326710d3830"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e58876c91f97b0952eb766123bfef372792ab3f4e3e1f1a2267834c2ab131734"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:224aaa38177597bb179f3ec87eeefcce8e4f85e608025e9cfac60de237ba6316"}, + {file = "pillow-11.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5bd2d3bdb846d757055910f0a59792d33b555800813c3b39ada1829c372ccb06"}, + {file = "pillow-11.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:375b8dd15a1f5d2feafff536d47e22f69625c1aa92f12b339ec0b2ca40263273"}, + {file = "pillow-11.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:daffdf51ee5db69a82dd127eabecce20729e21f7a3680cf7cbb23f0829189790"}, + {file = "pillow-11.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7326a1787e3c7b0429659e0a944725e1b03eeaa10edd945a86dead1913383944"}, + {file = "pillow-11.0.0.tar.gz", hash = "sha256:72bacbaf24ac003fea9bff9837d1eedb6088758d41e100c1552930151f677739"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=8.1)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] +fpx = ["olefile"] +mic = ["olefile"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] +typing = ["typing-extensions"] +xmp = ["defusedxml"] + +[[package]] +name = "platformdirs" +version = "4.3.6" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, +] + +[package.extras] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.11.2)"] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pre-commit" +version = "4.0.1" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +optional = false +python-versions = ">=3.9" +files = [ + {file = "pre_commit-4.0.1-py2.py3-none-any.whl", hash = "sha256:efde913840816312445dc98787724647c65473daefe420785f885e8ed9a06878"}, + {file = "pre_commit-4.0.1.tar.gz", hash = "sha256:80905ac375958c0444c65e9cebebd948b3cdb518f335a091a670a89d652139d2"}, +] + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +virtualenv = ">=20.10.0" + +[[package]] +name = "progress" +version = "1.6" +description = "Easy to use progress bars" +optional = false +python-versions = "*" +files = [ + {file = "progress-1.6.tar.gz", hash = "sha256:c9c86e98b5c03fa1fe11e3b67c1feda4788b8d0fe7336c2ff7d5644ccfba34cd"}, +] + +[[package]] +name = "pybullet" +version = "3.2.6" +description = "Official Python Interface for the Bullet Physics SDK specialized for Robotics Simulation and Reinforcement Learning" +optional = false +python-versions = "*" +files = [ + {file = "pybullet-3.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f71d0bed6824875b521066571d612274f1d2dead3ede50f8fbdde1946ec0c40"}, + {file = "pybullet-3.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a476d51726efa7aabe1dc8c4ee676b78bcd9fc19d4a2ce9776f2c9a9dacd9e8b"}, + {file = "pybullet-3.2.6-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8af791050e927f407a85fa9de15754fbe0973f8c798fd2893773b52fa1fc70e0"}, + {file = "pybullet-3.2.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72be1e68c61bbe49af5b7c609d59ede92d3fbbfca072e9b31d4819575c8f332b"}, + {file = "pybullet-3.2.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba46f9d8ae76d2bcae4bf4b5768b1dccc0b3b15110b69ba3d78cd0b11f8f21cf"}, + {file = "pybullet-3.2.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f928444471186c5d6092d71371b03436e23ec96acd7800dfefcd8e96572c6581"}, + {file = "pybullet-3.2.6.tar.gz", hash = "sha256:da27525433c88698dc9fd8bc20fa4ae4d07738b4656633659ebd82c2d2884e08"}, +] + +[[package]] +name = "pygments" +version = "2.18.0" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, + {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pyparsing" +version = "3.2.0" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pyparsing-3.2.0-py3-none-any.whl", hash = "sha256:93d9577b88da0bbea8cc8334ee8b918ed014968fd2ec383e868fb8afb1ccef84"}, + {file = "pyparsing-3.2.0.tar.gz", hash = "sha256:cbf74e27246d595d9a74b186b810f6fbb86726dbf3b9532efb343f6d7294fe9c"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "pyright" +version = "1.1.391" +description = "Command line wrapper for pyright" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyright-1.1.391-py3-none-any.whl", hash = "sha256:54fa186f8b3e8a55a44ebfa842636635688670c6896dcf6cf4a7fc75062f4d15"}, + {file = "pyright-1.1.391.tar.gz", hash = "sha256:66b2d42cdf5c3cbab05f2f4b76e8bec8aa78e679bfa0b6ad7b923d9e027cadb2"}, +] + +[package.dependencies] +nodeenv = ">=1.6.0" +typing-extensions = ">=4.1" + +[package.extras] +all = ["nodejs-wheel-binaries", "twine (>=3.4.1)"] +dev = ["twine (>=3.4.1)"] +nodejs = ["nodejs-wheel-binaries"] + +[[package]] +name = "pytest" +version = "8.3.4" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, + {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.5,<2" + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "6.0.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.9" +files = [ + {file = "pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0"}, + {file = "pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35"}, +] + +[package.dependencies] +coverage = {version = ">=7.5", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pyyaml" +version = "6.0.2" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, + {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, + {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, + {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, + {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, + {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, + {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, + {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, + {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, + {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, + {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, + {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, + {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, + {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, + {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, + {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, +] + +[[package]] +name = "qpsolvers" +version = "4.4.0" +description = "Quadratic programming solvers in Python with a unified API." +optional = false +python-versions = ">=3.7" +files = [ + {file = "qpsolvers-4.4.0-py3-none-any.whl", hash = "sha256:d63a4cd63dc4b3cd8a8bf5cf8593998767cdb41b017eed381ff3090d74dc34b5"}, + {file = "qpsolvers-4.4.0.tar.gz", hash = "sha256:bd2dc68f1c935d17d64ec35c8f6e9d9283a92bc37a92633956a64387dbb7a2b8"}, +] + +[package.dependencies] +numpy = ">=1.15.4" +scipy = ">=1.2.0" + +[package.extras] +clarabel = ["clarabel (>=0.4.1)"] +cvxopt = ["cvxopt (>=1.2.6)"] +daqp = ["daqp (>=0.5.1)"] +ecos = ["ecos (>=2.0.8)"] +gurobi = ["gurobipy (>=9.5.2)"] +highs = ["highspy (>=1.1.2.dev3)"] +mosek = ["cvxopt (>=1.2.6)", "mosek (>=10.0.40)"] +open-source-solvers = ["qpsolvers[clarabel,cvxopt,daqp,ecos,highs,osqp,piqp,proxqp,qpalm,qpax,quadprog,scs]"] +osqp = ["osqp (>=0.6.2)"] +piqp = ["piqp (>=0.2.2)"] +proxqp = ["proxsuite (>=0.2.9)"] +qpalm = ["qpalm (>=1.2.1)"] +qpax = ["qpax (>=0.0.9)"] +quadprog = ["quadprog (>=0.1.11)"] +scs = ["scs (>=3.2.0)"] +wheels-only = ["qpsolvers[cvxopt,daqp,ecos,highs,piqp,proxqp,qpalm]"] + +[[package]] +name = "quadprog" +version = "0.1.13" +description = "Quadratic Programming Solver" +optional = false +python-versions = ">=3.9" +files = [ + {file = "quadprog-0.1.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:25fda45c9fbcfa832d30ff54fff5f499572aa9f258bcb33d0ddd767a84aa57c8"}, + {file = "quadprog-0.1.13-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e45501cf7765b47abb4cba100b695cae1a5f110df157209d0a123f17634225ed"}, + {file = "quadprog-0.1.13-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a33d2171f3f7f608ae2cce3ff8a79aa906902f79d57c1fc9b68ce3d88ba0f410"}, + {file = "quadprog-0.1.13-cp310-cp310-win_amd64.whl", hash = "sha256:bb10f5ce4f0c006a8b185f8b1b474175edcfd7005a7cefa5db2d9d19f08f6bed"}, + {file = "quadprog-0.1.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d310a08c9b119312bef2931bea452d9b7ad4430663d3a88a9bc642881f2cb8cf"}, + {file = "quadprog-0.1.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:415fa9e371ccdcf2142333f0991c0983aebf84ce8c8f584f822729394c31d399"}, + {file = "quadprog-0.1.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:303e6c726a483db04d5d8a43d1294b095ff862e6a4eeccf1f7df2c27f7c489e5"}, + {file = "quadprog-0.1.13-cp311-cp311-win_amd64.whl", hash = "sha256:34d5050a3de160a096d1b432a0c42f9dcce563ec5eb21841a883ecf67652f2dd"}, + {file = "quadprog-0.1.13-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cdae336347fc7488743739ad39943f60976ada74cacd0892f5d1503152359ced"}, + {file = "quadprog-0.1.13-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8628ea1b1910c0fbfc828fca36ebc76a8c7352fdef782b5971aee1f4db08ba38"}, + {file = "quadprog-0.1.13-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e2cb007ecf649fb2a614a1954359d534682d7339c3834df30aee7321ae1f7bb"}, + {file = "quadprog-0.1.13-cp312-cp312-win_amd64.whl", hash = "sha256:d3d8d774d6436829ea4a11eeffe4a1a72375df17c9e9a7a48973372e64c826ad"}, + {file = "quadprog-0.1.13-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec18372bff2a07a98f82b28ce1f41a31589b9aebbb69bc164ac6869a5e5a6b1a"}, + {file = "quadprog-0.1.13-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4f456782f44ed168a32104871e83e9e9d1d07c8ca4d7e72dc9a470e8ba39a3b0"}, + {file = "quadprog-0.1.13-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc1996a0e3de1d423f8662fe21368948afdc91d851910b77320caaf7c15357ff"}, + {file = "quadprog-0.1.13-cp313-cp313-win_amd64.whl", hash = "sha256:f8edf2b08aeee5d824ee4da4cfb2d3ac56e580c2b10ae132b7d1a45717c0bd92"}, + {file = "quadprog-0.1.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1f6cb8075a641e9a33254dc3478fd63255dd9074f326d2b2db5a970d30cb8809"}, + {file = "quadprog-0.1.13-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b80494e9c06f2032add6c22567e69f2df94b4ed5f0ffe3168ea5c5fc05dd0055"}, + {file = "quadprog-0.1.13-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ada11e909bda79657ed2727f1a3682dc80082c917d5fb2cee2593d344cd6d725"}, + {file = "quadprog-0.1.13-cp39-cp39-win_amd64.whl", hash = "sha256:0260385fc7ebe88a36c964c66c4a10e5ee05ef66a8fd73859ea3fb5aca284826"}, + {file = "quadprog-0.1.13.tar.gz", hash = "sha256:9d6dd32f2762f29b840fb83741d11e527ddf48745f63b79caad0e530b4a6a0ff"}, +] + +[package.dependencies] +numpy = "*" + +[[package]] +name = "requests" +version = "2.32.3" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.8" +files = [ + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "rtb-data" +version = "1.0.1" +description = "Data files for the Robotics Toolbox for Python." +optional = false +python-versions = "*" +files = [ + {file = "rtb-data-1.0.1.tar.gz", hash = "sha256:c51292ddcdf5962e59456c3a5ab62a4d540a5ea5bdd4e35b2963f9ec3825cf1d"}, +] + +[[package]] +name = "ruff" +version = "0.8.4" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.8.4-py3-none-linux_armv6l.whl", hash = "sha256:58072f0c06080276804c6a4e21a9045a706584a958e644353603d36ca1eb8a60"}, + {file = "ruff-0.8.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ffb60904651c00a1e0b8df594591770018a0f04587f7deeb3838344fe3adabac"}, + {file = "ruff-0.8.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6ddf5d654ac0d44389f6bf05cee4caeefc3132a64b58ea46738111d687352296"}, + {file = "ruff-0.8.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e248b1f0fa2749edd3350a2a342b67b43a2627434c059a063418e3d375cfe643"}, + {file = "ruff-0.8.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf197b98ed86e417412ee3b6c893f44c8864f816451441483253d5ff22c0e81e"}, + {file = "ruff-0.8.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c41319b85faa3aadd4d30cb1cffdd9ac6b89704ff79f7664b853785b48eccdf3"}, + {file = "ruff-0.8.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:9f8402b7c4f96463f135e936d9ab77b65711fcd5d72e5d67597b543bbb43cf3f"}, + {file = "ruff-0.8.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4e56b3baa9c23d324ead112a4fdf20db9a3f8f29eeabff1355114dd96014604"}, + {file = "ruff-0.8.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:736272574e97157f7edbbb43b1d046125fce9e7d8d583d5d65d0c9bf2c15addf"}, + {file = "ruff-0.8.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5fe710ab6061592521f902fca7ebcb9fabd27bc7c57c764298b1c1f15fff720"}, + {file = "ruff-0.8.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:13e9ec6d6b55f6da412d59953d65d66e760d583dd3c1c72bf1f26435b5bfdbae"}, + {file = "ruff-0.8.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:97d9aefef725348ad77d6db98b726cfdb075a40b936c7984088804dfd38268a7"}, + {file = "ruff-0.8.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ab78e33325a6f5374e04c2ab924a3367d69a0da36f8c9cb6b894a62017506111"}, + {file = "ruff-0.8.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8ef06f66f4a05c3ddbc9121a8b0cecccd92c5bf3dd43b5472ffe40b8ca10f0f8"}, + {file = "ruff-0.8.4-py3-none-win32.whl", hash = "sha256:552fb6d861320958ca5e15f28b20a3d071aa83b93caee33a87b471f99a6c0835"}, + {file = "ruff-0.8.4-py3-none-win_amd64.whl", hash = "sha256:f21a1143776f8656d7f364bd264a9d60f01b7f52243fbe90e7670c0dfe0cf65d"}, + {file = "ruff-0.8.4-py3-none-win_arm64.whl", hash = "sha256:9183dd615d8df50defa8b1d9a074053891ba39025cf5ae88e8bcb52edcc4bf08"}, + {file = "ruff-0.8.4.tar.gz", hash = "sha256:0d5f89f254836799af1615798caa5f80b7f935d7a670fad66c5007928e57ace8"}, +] + +[[package]] +name = "scipy" +version = "1.14.1" +description = "Fundamental algorithms for scientific computing in Python" +optional = false +python-versions = ">=3.10" +files = [ + {file = "scipy-1.14.1-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:b28d2ca4add7ac16ae8bb6632a3c86e4b9e4d52d3e34267f6e1b0c1f8d87e389"}, + {file = "scipy-1.14.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d0d2821003174de06b69e58cef2316a6622b60ee613121199cb2852a873f8cf3"}, + {file = "scipy-1.14.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8bddf15838ba768bb5f5083c1ea012d64c9a444e16192762bd858f1e126196d0"}, + {file = "scipy-1.14.1-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:97c5dddd5932bd2a1a31c927ba5e1463a53b87ca96b5c9bdf5dfd6096e27efc3"}, + {file = "scipy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ff0a7e01e422c15739ecd64432743cf7aae2b03f3084288f399affcefe5222d"}, + {file = "scipy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e32dced201274bf96899e6491d9ba3e9a5f6b336708656466ad0522d8528f69"}, + {file = "scipy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8426251ad1e4ad903a4514712d2fa8fdd5382c978010d1c6f5f37ef286a713ad"}, + {file = "scipy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:a49f6ed96f83966f576b33a44257d869756df6cf1ef4934f59dd58b25e0327e5"}, + {file = "scipy-1.14.1-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:2da0469a4ef0ecd3693761acbdc20f2fdeafb69e6819cc081308cc978153c675"}, + {file = "scipy-1.14.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:c0ee987efa6737242745f347835da2cc5bb9f1b42996a4d97d5c7ff7928cb6f2"}, + {file = "scipy-1.14.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3a1b111fac6baec1c1d92f27e76511c9e7218f1695d61b59e05e0fe04dc59617"}, + {file = "scipy-1.14.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8475230e55549ab3f207bff11ebfc91c805dc3463ef62eda3ccf593254524ce8"}, + {file = "scipy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:278266012eb69f4a720827bdd2dc54b2271c97d84255b2faaa8f161a158c3b37"}, + {file = "scipy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fef8c87f8abfb884dac04e97824b61299880c43f4ce675dd2cbeadd3c9b466d2"}, + {file = "scipy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b05d43735bb2f07d689f56f7b474788a13ed8adc484a85aa65c0fd931cf9ccd2"}, + {file = "scipy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:716e389b694c4bb564b4fc0c51bc84d381735e0d39d3f26ec1af2556ec6aad94"}, + {file = "scipy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:631f07b3734d34aced009aaf6fedfd0eb3498a97e581c3b1e5f14a04164a456d"}, + {file = "scipy-1.14.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:af29a935803cc707ab2ed7791c44288a682f9c8107bc00f0eccc4f92c08d6e07"}, + {file = "scipy-1.14.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:2843f2d527d9eebec9a43e6b406fb7266f3af25a751aa91d62ff416f54170bc5"}, + {file = "scipy-1.14.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:eb58ca0abd96911932f688528977858681a59d61a7ce908ffd355957f7025cfc"}, + {file = "scipy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30ac8812c1d2aab7131a79ba62933a2a76f582d5dbbc695192453dae67ad6310"}, + {file = "scipy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f9ea80f2e65bdaa0b7627fb00cbeb2daf163caa015e59b7516395fe3bd1e066"}, + {file = "scipy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:edaf02b82cd7639db00dbff629995ef185c8df4c3ffa71a5562a595765a06ce1"}, + {file = "scipy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:2ff38e22128e6c03ff73b6bb0f85f897d2362f8c052e3b8ad00532198fbdae3f"}, + {file = "scipy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1729560c906963fc8389f6aac023739ff3983e727b1a4d87696b7bf108316a79"}, + {file = "scipy-1.14.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:4079b90df244709e675cdc8b93bfd8a395d59af40b72e339c2287c91860deb8e"}, + {file = "scipy-1.14.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e0cf28db0f24a38b2a0ca33a85a54852586e43cf6fd876365c86e0657cfe7d73"}, + {file = "scipy-1.14.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0c2f95de3b04e26f5f3ad5bb05e74ba7f68b837133a4492414b3afd79dfe540e"}, + {file = "scipy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b99722ea48b7ea25e8e015e8341ae74624f72e5f21fc2abd45f3a93266de4c5d"}, + {file = "scipy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5149e3fd2d686e42144a093b206aef01932a0059c2a33ddfa67f5f035bdfe13e"}, + {file = "scipy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4f5a7c49323533f9103d4dacf4e4f07078f360743dec7f7596949149efeec06"}, + {file = "scipy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:baff393942b550823bfce952bb62270ee17504d02a1801d7fd0719534dfb9c84"}, + {file = "scipy-1.14.1.tar.gz", hash = "sha256:5a275584e726026a5699459aa72f828a610821006228e841b94275c4a7c08417"}, +] + +[package.dependencies] +numpy = ">=1.23.5,<2.3" + +[package.extras] +dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy (==1.10.0)", "pycodestyle", "pydevtool", "rich-click", "ruff (>=0.0.292)", "types-psutil", "typing_extensions"] +doc = ["jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.13.1)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0,<=7.3.7)", "sphinx-design (>=0.4.0)"] +test = ["Cython", "array-api-strict (>=2.0)", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] + +[[package]] +name = "six" +version = "1.17.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, +] + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +optional = false +python-versions = "*" +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] + +[[package]] +name = "spatialgeometry" +version = "1.1.0" +description = "A Shape and Geometry Description Package" +optional = false +python-versions = ">=3.7" +files = [ + {file = "spatialgeometry-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0764b44008e3797c6e154b653b4964ec9224836a1668a1e69909b4a46d2944d"}, + {file = "spatialgeometry-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d793425c7afbcc63f792d0cf1e2633b87055a8d9c31563ab049c24007572a08e"}, + {file = "spatialgeometry-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4cf28656881633a4165e1550473dc739797a7adc8b962b35391e6840e7d44447"}, + {file = "spatialgeometry-1.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fc38ea9ee1f858edb31f332faca00374dd1ba3f5affa767c220deefa5b24510"}, + {file = "spatialgeometry-1.1.0-cp310-cp310-win32.whl", hash = "sha256:14777a279f6ad20cef3c0384bb3f2861e5b549f6ed69efd0c7dd50dfb3f722ff"}, + {file = "spatialgeometry-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:56ecacb75a3a324a791916fe88ffff2a76f692a9b45eac8cd60febb5459bc484"}, + {file = "spatialgeometry-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fa65a83c0f337dd0f8a0079f49f79580759d1d600e51600a34e2ace6a68db671"}, + {file = "spatialgeometry-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:05cf9f0ea322b973b3ac90fd1470d0e69bd9d106edfaabb41c5c80b96cba3e3a"}, + {file = "spatialgeometry-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70932e6fb86cfe7ca71f044668ed569015b6bd7ed9e817e17492d756a4847ed1"}, + {file = "spatialgeometry-1.1.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2e8eeb0a0638044155d1815479c299419db7c5c7ae98b265117eccb65f2f8c6"}, + {file = "spatialgeometry-1.1.0-cp311-cp311-win32.whl", hash = "sha256:a4d8b9acad17937f4bf9f6bc932450b0f9700c3eb2c2a34ea465349536f50b07"}, + {file = "spatialgeometry-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:c16aacb679bee40b7a14744bc294f327d8adb140f5b989a5947aa7cd744bda2f"}, + {file = "spatialgeometry-1.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4aed62a6505d63d1859c8a909afc788ac588ba8f7b91afd024528fff5893a243"}, + {file = "spatialgeometry-1.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d21fe7f1b0a8905881a1dae53e8fbbb352f1cb2b31918bca95ec0f34d19141d"}, + {file = "spatialgeometry-1.1.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5edfff744d73cedee8b8742d691bbffb16248aa07448f9b2b78e41658ffa2d5c"}, + {file = "spatialgeometry-1.1.0-cp37-cp37m-win32.whl", hash = "sha256:5f0459c2a55fd152f46c3208c4ccc5b4c4cb7dc678460be3ac286fdc2814dc57"}, + {file = "spatialgeometry-1.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:7dafa40f67f5feb7086206f6a3e05b06f7f2a3dc65c040e3c3e70f191b007b47"}, + {file = "spatialgeometry-1.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:11254bca04a155f131117f4740918f2501512a5d7c61ff3f14d52468343a723c"}, + {file = "spatialgeometry-1.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e2be6e5c6e4efa8afd63e1462a37cb082459e47a929d34ad8efdec81639c09d0"}, + {file = "spatialgeometry-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f46b3cadd7bfaec54898795e36eda8d61850aebcd2bbb11054ecd07fea6092c9"}, + {file = "spatialgeometry-1.1.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b18b8646c0e8e5c69f8d5fb7bda9a5979f4f09d6164f4d9a7a4e52911bdca5dd"}, + {file = "spatialgeometry-1.1.0-cp38-cp38-win32.whl", hash = "sha256:768e7d5e5af9339fc0890213b30e19a4885f13ebc7c041ede3982b8210a183e2"}, + {file = "spatialgeometry-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:26ca10d6b24c91ca64f77201ef328ec346750565bbbf4b7a530689a9d66f523b"}, + {file = "spatialgeometry-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b0a7905df919b3d57c542ca0762ca2a07a5f993a039b1ce78d2255d01de01802"}, + {file = "spatialgeometry-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:63d2444e0eb9d2d49a99a7e54e22c5ba0dd776787d680608ca158641e3667917"}, + {file = "spatialgeometry-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc6002ebdf9ed6b11f2191a7e4f32988511534e197006717830ba805901e99ab"}, + {file = "spatialgeometry-1.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1b231de91ef3f40675d5b29ec8f4e0ac9530ba4c2f9f0f4d3759241ec72152d"}, + {file = "spatialgeometry-1.1.0-cp39-cp39-win32.whl", hash = "sha256:d133a1a6d4ddbbf1b9b0705f203e0ed98c0988ee1413ab180fac84bea579c8e4"}, + {file = "spatialgeometry-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:053af13bf4847417353b12bb06199e94c6dd4a8cb87bfd3624af0ad2960d70e5"}, + {file = "spatialgeometry-1.1.0.tar.gz", hash = "sha256:98b2b1023eed6197d26f31cf4643c51d1a41059ac6036a10b16dc389ec6af5a6"}, +] + +[package.dependencies] +spatialmath-python = ">=1.0.0" + +[package.extras] +collision = ["pybullet"] +dev = ["black", "flake8", "pytest", "pytest-cov", "pyyaml", "roboticstoolbox-python (>=1.0.0,<1.1.0)", "swift-sim"] +docs = ["sphinx", "sphinx-autorun", "sphinx-rtd-theme"] + +[[package]] +name = "spatialmath-python" +version = "1.1.13" +description = "Provides spatial maths capability for Python." +optional = false +python-versions = ">=3.11" +files = [] +develop = false + +[package.dependencies] +ansitable = "*" +colored = "^2.2.4" +matplotlib = ">=3.9.3" +numpy = ">=2.1.0" +pre-commit = "*" +pyright = "^1.1.390" +scipy = "*" +sympy = "^1.13.3" +typing_extensions = "*" + +[package.source] +type = "directory" +url = "../spatialmath-python" + +[[package]] +name = "sphinx" +version = "8.1.3" +description = "Python documentation generator" +optional = false +python-versions = ">=3.10" +files = [ + {file = "sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2"}, + {file = "sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927"}, +] + +[package.dependencies] +alabaster = ">=0.7.14" +babel = ">=2.13" +colorama = {version = ">=0.4.6", markers = "sys_platform == \"win32\""} +docutils = ">=0.20,<0.22" +imagesize = ">=1.3" +Jinja2 = ">=3.1" +packaging = ">=23.0" +Pygments = ">=2.17" +requests = ">=2.30.0" +snowballstemmer = ">=2.2" +sphinxcontrib-applehelp = ">=1.0.7" +sphinxcontrib-devhelp = ">=1.0.6" +sphinxcontrib-htmlhelp = ">=2.0.6" +sphinxcontrib-jsmath = ">=1.0.1" +sphinxcontrib-qthelp = ">=1.0.6" +sphinxcontrib-serializinghtml = ">=1.1.9" + +[package.extras] +docs = ["sphinxcontrib-websupport"] +lint = ["flake8 (>=6.0)", "mypy (==1.11.1)", "pyright (==1.1.384)", "pytest (>=6.0)", "ruff (==0.6.9)", "sphinx-lint (>=0.9)", "tomli (>=2)", "types-Pillow (==10.2.0.20240822)", "types-Pygments (==2.18.0.20240506)", "types-colorama (==0.4.15.20240311)", "types-defusedxml (==0.7.0.20240218)", "types-docutils (==0.21.0.20241005)", "types-requests (==2.32.0.20240914)", "types-urllib3 (==1.26.25.14)"] +test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=8.0)", "setuptools (>=70.0)", "typing_extensions (>=4.9)"] + +[[package]] +name = "sphinx-autodoc-typehints" +version = "2.5.0" +description = "Type hints (PEP 484) support for the Sphinx autodoc extension" +optional = false +python-versions = ">=3.10" +files = [ + {file = "sphinx_autodoc_typehints-2.5.0-py3-none-any.whl", hash = "sha256:53def4753239683835b19bfa8b68c021388bd48a096efcb02cdab508ece27363"}, + {file = "sphinx_autodoc_typehints-2.5.0.tar.gz", hash = "sha256:259e1026b218d563d72743f417fcc25906a9614897fe37f91bd8d7d58f748c3b"}, +] + +[package.dependencies] +sphinx = ">=8.0.2" + +[package.extras] +docs = ["furo (>=2024.8.6)"] +numpy = ["nptyping (>=2.5)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "defusedxml (>=0.7.1)", "diff-cover (>=9.1.1)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "sphobjinv (>=2.3.1.1)", "typing-extensions (>=4.12.2)"] + +[[package]] +name = "sphinx-autorun" +version = "1.1.1" +description = "Sphinx extension autorun" +optional = false +python-versions = "*" +files = [ + {file = "sphinx-autorun-1.1.1.tar.gz", hash = "sha256:61a39b604487c6dbfd601b3814443bb554532617abd39563e8d603c01ccc711a"}, + {file = "sphinx_autorun-1.1.1-py3-none-any.whl", hash = "sha256:3a2869641e89f693a4cae1a461a8bf1b2492e34e0f1f86f372eb849842c6f258"}, +] + +[package.dependencies] +Sphinx = ">=0.6" + +[[package]] +name = "sphinx-favicon" +version = "1.0.1" +description = "Sphinx Extension adding support for custom favicons" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sphinx-favicon-1.0.1.tar.gz", hash = "sha256:df796de32125609c1b4a8964db74270ebf4502089c27cd53f542354dc0b57e8e"}, + {file = "sphinx_favicon-1.0.1-py3-none-any.whl", hash = "sha256:7c93d6b634cb4c9687ceab67a8526f05d3b02679df94e273e51a43282e6b034c"}, +] + +[package.dependencies] +sphinx = ">=3.4" + +[package.extras] +dev = ["nox", "pre-commit"] +doc = ["pydata-sphinx-theme", "sphinx (<6)", "sphinx-copybutton", "sphinx-design"] +test = ["beautifulsoup4", "pytest", "pytest-cov"] + +[[package]] +name = "sphinx-rtd-theme" +version = "3.0.2" +description = "Read the Docs theme for Sphinx" +optional = false +python-versions = ">=3.8" +files = [ + {file = "sphinx_rtd_theme-3.0.2-py2.py3-none-any.whl", hash = "sha256:422ccc750c3a3a311de4ae327e82affdaf59eb695ba4936538552f3b00f4ee13"}, + {file = "sphinx_rtd_theme-3.0.2.tar.gz", hash = "sha256:b7457bc25dda723b20b086a670b9953c859eab60a2a03ee8eb2bb23e176e5f85"}, +] + +[package.dependencies] +docutils = ">0.18,<0.22" +sphinx = ">=6,<9" +sphinxcontrib-jquery = ">=4,<5" + +[package.extras] +dev = ["bump2version", "transifex-client", "twine", "wheel"] + +[[package]] +name = "sphinxcontrib-applehelp" +version = "2.0.0" +description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5"}, + {file = "sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1"}, +] + +[package.extras] +lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] +standalone = ["Sphinx (>=5)"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "2.0.0" +description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2"}, + {file = "sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad"}, +] + +[package.extras] +lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] +standalone = ["Sphinx (>=5)"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.1.0" +description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8"}, + {file = "sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9"}, +] + +[package.extras] +lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] +standalone = ["Sphinx (>=5)"] +test = ["html5lib", "pytest"] + +[[package]] +name = "sphinxcontrib-jquery" +version = "4.1" +description = "Extension to include jQuery on newer Sphinx releases" +optional = false +python-versions = ">=2.7" +files = [ + {file = "sphinxcontrib-jquery-4.1.tar.gz", hash = "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a"}, + {file = "sphinxcontrib_jquery-4.1-py2.py3-none-any.whl", hash = "sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae"}, +] + +[package.dependencies] +Sphinx = ">=1.8" + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +description = "A sphinx extension which renders display math in HTML via JavaScript" +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, + {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, +] + +[package.extras] +test = ["flake8", "mypy", "pytest"] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "2.0.0" +description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb"}, + {file = "sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab"}, +] + +[package.extras] +lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] +standalone = ["Sphinx (>=5)"] +test = ["defusedxml (>=0.7.1)", "pytest"] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "2.0.0" +description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331"}, + {file = "sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d"}, +] + +[package.extras] +lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] +standalone = ["Sphinx (>=5)"] +test = ["pytest"] + +[[package]] +name = "swift-sim" +version = "1.1.0" +description = "A Python/Javascript Robot Simulator and Visualiser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "swift-sim-1.1.0.tar.gz", hash = "sha256:77a99348419ed2ca854f695e1328e5c313a2fdac612ed4b2406e2551de14f7d0"}, + {file = "swift_sim-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:45699d8b9936a4d2b3d5a6df9847158efb0cd876363157181680ef56fa20f5ee"}, + {file = "swift_sim-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3c7c8bcbaf3408cf5d0f3fd21b5bb32e34c8a95bf34f5d9e25eb1ac354b39621"}, + {file = "swift_sim-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c6707cdfcef69e70ab658b817bc6ac6e40151b4047598de503ebdadacc76c63"}, + {file = "swift_sim-1.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7862a81c087084b58f0284402975a426337285547aa7fa6329f5ee1846bbd3c1"}, + {file = "swift_sim-1.1.0-cp310-cp310-win32.whl", hash = "sha256:e5a045e3a81694825ea20e2aec8914908af1db79f22ba2aa5cc6f943a39b8ff3"}, + {file = "swift_sim-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0598def99601b45b39e5547bfff7520f47200288ccb4af1dea8563dc176ccafa"}, + {file = "swift_sim-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:07fbd7bea5016efc4fa309e885f5b34a2660577221232b455036159b00881f4e"}, + {file = "swift_sim-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:47c63c23763af1a6957c9ad360600f0d1f3ee2b152195c87bb81add44beaab9e"}, + {file = "swift_sim-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a670b3d73f8c28b27cdd91da4cd81f20009457a3418f1ce7d8e41e816520aad7"}, + {file = "swift_sim-1.1.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b199a7e26fce68f0323246caacf3b9ec135d652f09d1acab09c1232c8bf3d1c"}, + {file = "swift_sim-1.1.0-cp311-cp311-win32.whl", hash = "sha256:e0a62c17ca7215237bd3543e8b61b810720f3fae1d230eaffb674863dcd03f53"}, + {file = "swift_sim-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:0c2bdaa9d95f76fa40e35e9159e3633a74bb8cc5e66dd467fc708a2e1a454c09"}, + {file = "swift_sim-1.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fc669590df02fccd2a920180b91a73e39316de337c93dde4729f8b6896c5bb59"}, + {file = "swift_sim-1.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56d530b4d6cc56bfe4c1dca47d0f907d5b745ae2d4ac929698bda27c3695b993"}, + {file = "swift_sim-1.1.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:256c6b8eed4405cc315857c8d6f4c2e99396ef7b18e0e009785630e4adfdfd31"}, + {file = "swift_sim-1.1.0-cp37-cp37m-win32.whl", hash = "sha256:54096ec173d336a2e7df0d0e5d7c67b0fdd1670124fc7d31687bda8f5079c124"}, + {file = "swift_sim-1.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:694f6149e5fba1aabb51836f8d16a37fc564b4cf27120b6ffddf954650305c2e"}, + {file = "swift_sim-1.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:22d18c4e96e5ec1a49693ebffc81accc7ae5c02354cd49bcb45d8169e1d0c725"}, + {file = "swift_sim-1.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:db323f1c0f9fae867b0dfb7a4c797ad158bbdfb9b9190e8278ccd82ece32607d"}, + {file = "swift_sim-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4dfa4203727bb9fba3fa913a8081d034fb4d4875a6b43258c6f3eb3cbcdeb4"}, + {file = "swift_sim-1.1.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85e62b241a938f98e69d1a323567523fd901feef1fad7fa5a4d1e5890a81ed48"}, + {file = "swift_sim-1.1.0-cp38-cp38-win32.whl", hash = "sha256:714d4571e7d4808d5950ba64cd1f572df72da5f2cf7fd8da3f23597d5bcc8367"}, + {file = "swift_sim-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:0f199d4f39cca6dd30f31be1782a3d808a6a6bc071b8b65da6cae0fa4aa5ced6"}, + {file = "swift_sim-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:527acec185a516d8e52c7265b7c95e4df8d31db8356e47cde93bfc2cf87669ff"}, + {file = "swift_sim-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ba6e07ed53333ee42e220ba1e81f6302a91b42a2c300730e9cebf47178eca4c7"}, + {file = "swift_sim-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bbba6fa14a17defefa3fe487cc1dc2c17a40f86dfa7d09df312ce9f1392b0f8"}, + {file = "swift_sim-1.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e324e96cf2c027a5e62bb6437b8234a49460e45b246a460b3e70742dbbe2119d"}, + {file = "swift_sim-1.1.0-cp39-cp39-win32.whl", hash = "sha256:eda319fb4c6b913bf97afdab6bdf1e5f01283ae0853a6debae5cbc8546cc0b12"}, + {file = "swift_sim-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:20371492a8a79d9732083607781ccce56d2ba713fe46e6c38a95eb7ccb78e312"}, +] + +[package.dependencies] +numpy = ">=1.17.4" +spatialgeometry = ">=1.0.0" +websockets = "*" + +[package.extras] +dev = ["black", "flake8", "pytest", "pytest-cov", "pyyaml", "roboticstoolbox-python (>=1.0.0,<1.1.0)", "swift-sim"] +docs = ["sphinx", "sphinx-autorun", "sphinx-rtd-theme"] +nb = ["ipython", "notebook"] +vision = ["aiortc", "opencv-python"] + +[[package]] +name = "sympy" +version = "1.13.3" +description = "Computer algebra system (CAS) in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "sympy-1.13.3-py3-none-any.whl", hash = "sha256:54612cf55a62755ee71824ce692986f23c88ffa77207b30c1368eda4a7060f73"}, + {file = "sympy-1.13.3.tar.gz", hash = "sha256:b27fd2c6530e0ab39e275fc9b683895367e51d5da91baa8d3d64db2565fec4d9"}, +] + +[package.dependencies] +mpmath = ">=1.1.0,<1.4" + +[package.extras] +dev = ["hypothesis (>=6.70.0)", "pytest (>=7.1.0)"] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] + +[[package]] +name = "urllib3" +version = "2.2.3" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, + {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "virtualenv" +version = "20.28.0" +description = "Virtual Python Environment builder" +optional = false +python-versions = ">=3.8" +files = [ + {file = "virtualenv-20.28.0-py3-none-any.whl", hash = "sha256:23eae1b4516ecd610481eda647f3a7c09aea295055337331bb4e6892ecce47b0"}, + {file = "virtualenv-20.28.0.tar.gz", hash = "sha256:2c9c3262bb8e7b87ea801d715fae4495e6032450c71d2309be9550e7364049aa"}, +] + +[package.dependencies] +distlib = ">=0.3.7,<1" +filelock = ">=3.12.2,<4" +platformdirs = ">=3.9.1,<5" + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] + +[[package]] +name = "websockets" +version = "14.1" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +optional = false +python-versions = ">=3.9" +files = [ + {file = "websockets-14.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a0adf84bc2e7c86e8a202537b4fd50e6f7f0e4a6b6bf64d7ccb96c4cd3330b29"}, + {file = "websockets-14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90b5d9dfbb6d07a84ed3e696012610b6da074d97453bd01e0e30744b472c8179"}, + {file = "websockets-14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2177ee3901075167f01c5e335a6685e71b162a54a89a56001f1c3e9e3d2ad250"}, + {file = "websockets-14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f14a96a0034a27f9d47fd9788913924c89612225878f8078bb9d55f859272b0"}, + {file = "websockets-14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f874ba705deea77bcf64a9da42c1f5fc2466d8f14daf410bc7d4ceae0a9fcb0"}, + {file = "websockets-14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9607b9a442392e690a57909c362811184ea429585a71061cd5d3c2b98065c199"}, + {file = "websockets-14.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bea45f19b7ca000380fbd4e02552be86343080120d074b87f25593ce1700ad58"}, + {file = "websockets-14.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:219c8187b3ceeadbf2afcf0f25a4918d02da7b944d703b97d12fb01510869078"}, + {file = "websockets-14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ad2ab2547761d79926effe63de21479dfaf29834c50f98c4bf5b5480b5838434"}, + {file = "websockets-14.1-cp310-cp310-win32.whl", hash = "sha256:1288369a6a84e81b90da5dbed48610cd7e5d60af62df9851ed1d1d23a9069f10"}, + {file = "websockets-14.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0744623852f1497d825a49a99bfbec9bea4f3f946df6eb9d8a2f0c37a2fec2e"}, + {file = "websockets-14.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:449d77d636f8d9c17952628cc7e3b8faf6e92a17ec581ec0c0256300717e1512"}, + {file = "websockets-14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a35f704be14768cea9790d921c2c1cc4fc52700410b1c10948511039be824aac"}, + {file = "websockets-14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b1f3628a0510bd58968c0f60447e7a692933589b791a6b572fcef374053ca280"}, + {file = "websockets-14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c3deac3748ec73ef24fc7be0b68220d14d47d6647d2f85b2771cb35ea847aa1"}, + {file = "websockets-14.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7048eb4415d46368ef29d32133134c513f507fff7d953c18c91104738a68c3b3"}, + {file = "websockets-14.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6cf0ad281c979306a6a34242b371e90e891bce504509fb6bb5246bbbf31e7b6"}, + {file = "websockets-14.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cc1fc87428c1d18b643479caa7b15db7d544652e5bf610513d4a3478dbe823d0"}, + {file = "websockets-14.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f95ba34d71e2fa0c5d225bde3b3bdb152e957150100e75c86bc7f3964c450d89"}, + {file = "websockets-14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9481a6de29105d73cf4515f2bef8eb71e17ac184c19d0b9918a3701c6c9c4f23"}, + {file = "websockets-14.1-cp311-cp311-win32.whl", hash = "sha256:368a05465f49c5949e27afd6fbe0a77ce53082185bbb2ac096a3a8afaf4de52e"}, + {file = "websockets-14.1-cp311-cp311-win_amd64.whl", hash = "sha256:6d24fc337fc055c9e83414c94e1ee0dee902a486d19d2a7f0929e49d7d604b09"}, + {file = "websockets-14.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ed907449fe5e021933e46a3e65d651f641975a768d0649fee59f10c2985529ed"}, + {file = "websockets-14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:87e31011b5c14a33b29f17eb48932e63e1dcd3fa31d72209848652310d3d1f0d"}, + {file = "websockets-14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bc6ccf7d54c02ae47a48ddf9414c54d48af9c01076a2e1023e3b486b6e72c707"}, + {file = "websockets-14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9777564c0a72a1d457f0848977a1cbe15cfa75fa2f67ce267441e465717dcf1a"}, + {file = "websockets-14.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a655bde548ca98f55b43711b0ceefd2a88a71af6350b0c168aa77562104f3f45"}, + {file = "websockets-14.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3dfff83ca578cada2d19e665e9c8368e1598d4e787422a460ec70e531dbdd58"}, + {file = "websockets-14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6a6c9bcf7cdc0fd41cc7b7944447982e8acfd9f0d560ea6d6845428ed0562058"}, + {file = "websockets-14.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4b6caec8576e760f2c7dd878ba817653144d5f369200b6ddf9771d64385b84d4"}, + {file = "websockets-14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eb6d38971c800ff02e4a6afd791bbe3b923a9a57ca9aeab7314c21c84bf9ff05"}, + {file = "websockets-14.1-cp312-cp312-win32.whl", hash = "sha256:1d045cbe1358d76b24d5e20e7b1878efe578d9897a25c24e6006eef788c0fdf0"}, + {file = "websockets-14.1-cp312-cp312-win_amd64.whl", hash = "sha256:90f4c7a069c733d95c308380aae314f2cb45bd8a904fb03eb36d1a4983a4993f"}, + {file = "websockets-14.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:3630b670d5057cd9e08b9c4dab6493670e8e762a24c2c94ef312783870736ab9"}, + {file = "websockets-14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36ebd71db3b89e1f7b1a5deaa341a654852c3518ea7a8ddfdf69cc66acc2db1b"}, + {file = "websockets-14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5b918d288958dc3fa1c5a0b9aa3256cb2b2b84c54407f4813c45d52267600cd3"}, + {file = "websockets-14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00fe5da3f037041da1ee0cf8e308374e236883f9842c7c465aa65098b1c9af59"}, + {file = "websockets-14.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8149a0f5a72ca36720981418eeffeb5c2729ea55fa179091c81a0910a114a5d2"}, + {file = "websockets-14.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77569d19a13015e840b81550922056acabc25e3f52782625bc6843cfa034e1da"}, + {file = "websockets-14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cf5201a04550136ef870aa60ad3d29d2a59e452a7f96b94193bee6d73b8ad9a9"}, + {file = "websockets-14.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:88cf9163ef674b5be5736a584c999e98daf3aabac6e536e43286eb74c126b9c7"}, + {file = "websockets-14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:836bef7ae338a072e9d1863502026f01b14027250a4545672673057997d5c05a"}, + {file = "websockets-14.1-cp313-cp313-win32.whl", hash = "sha256:0d4290d559d68288da9f444089fd82490c8d2744309113fc26e2da6e48b65da6"}, + {file = "websockets-14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8621a07991add373c3c5c2cf89e1d277e49dc82ed72c75e3afc74bd0acc446f0"}, + {file = "websockets-14.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:01bb2d4f0a6d04538d3c5dfd27c0643269656c28045a53439cbf1c004f90897a"}, + {file = "websockets-14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:414ffe86f4d6f434a8c3b7913655a1a5383b617f9bf38720e7c0799fac3ab1c6"}, + {file = "websockets-14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8fda642151d5affdee8a430bd85496f2e2517be3a2b9d2484d633d5712b15c56"}, + {file = "websockets-14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd7c11968bc3860d5c78577f0dbc535257ccec41750675d58d8dc66aa47fe52c"}, + {file = "websockets-14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a032855dc7db987dff813583d04f4950d14326665d7e714d584560b140ae6b8b"}, + {file = "websockets-14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7e7ea2f782408c32d86b87a0d2c1fd8871b0399dd762364c731d86c86069a78"}, + {file = "websockets-14.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:39450e6215f7d9f6f7bc2a6da21d79374729f5d052333da4d5825af8a97e6735"}, + {file = "websockets-14.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ceada5be22fa5a5a4cdeec74e761c2ee7db287208f54c718f2df4b7e200b8d4a"}, + {file = "websockets-14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3fc753451d471cff90b8f467a1fc0ae64031cf2d81b7b34e1811b7e2691bc4bc"}, + {file = "websockets-14.1-cp39-cp39-win32.whl", hash = "sha256:14839f54786987ccd9d03ed7f334baec0f02272e7ec4f6e9d427ff584aeea8b4"}, + {file = "websockets-14.1-cp39-cp39-win_amd64.whl", hash = "sha256:d9fd19ecc3a4d5ae82ddbfb30962cf6d874ff943e56e0c81f5169be2fda62979"}, + {file = "websockets-14.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e5dc25a9dbd1a7f61eca4b7cb04e74ae4b963d658f9e4f9aad9cd00b688692c8"}, + {file = "websockets-14.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:04a97aca96ca2acedf0d1f332c861c5a4486fdcba7bcef35873820f940c4231e"}, + {file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df174ece723b228d3e8734a6f2a6febbd413ddec39b3dc592f5a4aa0aff28098"}, + {file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:034feb9f4286476f273b9a245fb15f02c34d9586a5bc936aff108c3ba1b21beb"}, + {file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:660c308dabd2b380807ab64b62985eaccf923a78ebc572bd485375b9ca2b7dc7"}, + {file = "websockets-14.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5a42d3ecbb2db5080fc578314439b1d79eef71d323dc661aa616fb492436af5d"}, + {file = "websockets-14.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ddaa4a390af911da6f680be8be4ff5aaf31c4c834c1a9147bc21cbcbca2d4370"}, + {file = "websockets-14.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a4c805c6034206143fbabd2d259ec5e757f8b29d0a2f0bf3d2fe5d1f60147a4a"}, + {file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:205f672a6c2c671a86d33f6d47c9b35781a998728d2c7c2a3e1cf3333fcb62b7"}, + {file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef440054124728cc49b01c33469de06755e5a7a4e83ef61934ad95fc327fbb0"}, + {file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7591d6f440af7f73c4bd9404f3772bfee064e639d2b6cc8c94076e71b2471c1"}, + {file = "websockets-14.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:25225cc79cfebc95ba1d24cd3ab86aaa35bcd315d12fa4358939bd55e9bd74a5"}, + {file = "websockets-14.1-py3-none-any.whl", hash = "sha256:4d4fc827a20abe6d544a119896f6b78ee13fe81cbfef416f3f2ddf09a03f0e2e"}, + {file = "websockets-14.1.tar.gz", hash = "sha256:398b10c77d471c0aab20a845e7a60076b6390bfdaac7a6d2edb0d2c59d75e8d8"}, +] + +[metadata] +lock-version = "2.0" +python-versions = ">=3.11" +content-hash = "7d32d4fa2348357df87f9687173d8da3d265195f6c8144ed34c041fc8aa7de86" diff --git a/pyproject.toml b/pyproject.toml index 29c7cc339..e218eb8f6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,204 +1,98 @@ -[project] - +[tool.poetry] name = "roboticstoolbox-python" - -description = "A Python library for robotics education and research" - version = "1.1.1" - +description = "A Python library for robotics education and research" authors = [ - { name = "Jesse Haviland", email = "j.haviland@qut.edu.au" }, - { name = "Peter Corke", email = "rvc@petercorke.com" }, -] - -dependencies = [ - "numpy>=1.17.4", - "spatialmath-python>=1.1.5", - "spatialgeometry>=1.0.0", - "pgraph-python", - "scipy", - "matplotlib", - "ansitable", - "swift-sim>=1.0.0", - "rtb-data", - "progress", - "typing_extensions", + "Jesse Haviland ", + "Peter Corke " ] - -license = { file = "LICENSE" } - +license = "MIT" readme = "README.md" - -requires-python = ">=3.7" - -keywords = [ - "python", - "robotics", - "robotics-toolbox", - "kinematics", - "dynamics", - "motion-planning", - "trajectory-generation", - "jacobian", - "hessian", - "control", - "simulation", - "robot-manipulator", - "mobile-robot", -] - +homepage = "https://github.com/petercorke/robotics-toolbox-python" +documentation = "https://petercorke.github.io/robotics-toolbox-python/" +repository = "https://github.com/petercorke/robotics-toolbox-python" +keywords = ["python", "robotics", "robotics-toolbox", "kinematics", "dynamics", "motion-planning", "trajectory-generation", "jacobian", "hessian", "control", "simulation", "robot-manipulator", "mobile-robot"] classifiers = [ "Development Status :: 5 - Production/Stable", - # Indicate who your project is intended for "Intended Audience :: Developers", - # Pick your license as you wish (should match "license" above) "License :: OSI Approved :: MIT License", - # Specify the Python versions you support here. "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", -] - - -[project.urls] -homepage = "https://github.com/petercorke/robotics-toolbox-python" -documentation = "https://petercorke.github.io/robotics-toolbox-python/" -repository = "https://github.com/petercorke/robotics-toolbox-python" - - -[project.optional-dependencies] - -collision = ["pybullet"] - -dev = [ - "black", - "pytest", - "pytest-cov", - "flake8", - "pyyaml", - "sympy", - "qpsolvers", - "quadprog", - "pybullet", - "bdsim", + "Programming Language :: Python :: 3.11" ] - -docs = [ - "sphinx", - "sphinx_rtd_theme", - "sphinx-autorun", - "sphinx_autodoc_typehints", - "sphinx-favicon", +packages = [ + { include = "roboticstoolbox" } ] - -[project.scripts] - +[tool.poetry.dependencies] +python = ">=3.11" +numpy = ">=2.1.3" +spatialmath-python = {path = "/home/robert/learning/forked_packages/spatialmath-python"} +spatialgeometry = ">=1.0.0" +sympy = "*" +pgraph-python = "*" +scipy = "*" +matplotlib = "*" +ansitable = "*" +swift-sim = ">=1.0.0" +rtb-data = "*" +progress = "*" +typing_extensions = "*" + +# Optional dependencies +pybullet = { version = "*", optional = true } + +[tool.poetry.group.dev.dependencies] +ruff = "*" +pytest = "*" +pytest-cov = "*" +pyyaml = "*" +qpsolvers = "*" +quadprog = "*" +pybullet = "*" +bdsim = "*" + +[tool.poetry.group.docs.dependencies] +sphinx = "*" +sphinx_rtd_theme = "*" +sphinx-autorun = "*" +sphinx_autodoc_typehints = "*" +sphinx-favicon = "*" + +[tool.poetry.scripts] eigdemo = "roboticstoolbox.examples.eigdemo:main" tripleangledemo = "roboticstoolbox.examples.tripleangledemo:main" twistdemo = "roboticstoolbox.examples.twistdemo:main" rtbtool = "roboticstoolbox.bin.rtbtool:main" - -[build-system] - -requires = ["setuptools", "oldest-supported-numpy"] -build-backend = "setuptools.build_meta" - - -[tool.setuptools] - -packages = [ - "roboticstoolbox", - "roboticstoolbox.backends", - "roboticstoolbox.backends.PyPlot", - "roboticstoolbox.backends.swift", - "roboticstoolbox.bin", - "roboticstoolbox.blocks", - "roboticstoolbox.examples", - "roboticstoolbox.bin", - "roboticstoolbox.robot", - "roboticstoolbox.mobile", - "roboticstoolbox.models", - "roboticstoolbox.models.DH", - "roboticstoolbox.models.ETS", - "roboticstoolbox.models.URDF", - "roboticstoolbox.tools", - "roboticstoolbox.tools.urdf", - "roboticstoolbox.tools.xacro", -] - - -[tool.black] - +[tool.ruff] line-length = 88 -target_version = ['py37'] -args = '--experimental-string-processing' - -include = '\.pyi?$' -exclude = ''' - -( - /( - \.eggs # exclude a few common directories in the - | \.git # root of the project - | \.hg - | \.mypy_cache - | \.tox - | \.venv - | \.github - | _build - | buck-out - | build - | dist - | docs - )/ -) -''' - - -[tool.flake8] - -ignore = ['F821', 'W503', 'E741'] -max-line-length = 88 -extend-ignore = 'E203' - - -[tool.cibuildwheel] - -# Will cause the wheel to be installed with `pip install [dev,collision]` -# test-extras = ["dev", "collision"] -# test-requires = "pytest" -# test-command = "pytest {project}/tests" - -manylinux-x86_64-image = "manylinux2014" -manylinux-aarch64-image = "manylinux2014" - -# Build CPython 3.7 - 3.11 -build = ["cp37-*", "cp38-*", "cp39-*", "cp310-*", "cp311-*"] - -# Disable building musllinux wheels on all platforms -skip = ["pp*", "*musllinux*"] - - -[tool.cibuildwheel.macos] - -# Build `x86_64` and `arm64` wheels on an Intel runner. -# Note that the `arm64` wheel cannot be tested in this configuration. -archs = ["x86_64", "arm64"] - -[tool.cibuildwheel.linux] - -# On an Linux Intel runner with qemu installed, build Intel and ARM wheels -archs = ["x86_64", "aarch64"] +target-version = "py311" +exclude = [ + ".eggs", + ".git", + ".hg", + ".mypy_cache", + ".tox", + ".venv", + ".github", + "_build", + "buck-out", + "build", + "dist", + "docs", + "**/*.ipynb" +] -[tool.cibuildwheel.windows] +[tool.ruff.lint] +ignore = ["E741", "E731"] -# On an Windows Intel runner build wheels -archs = ["AMD64", "x86"] +[tool.ruff.format] +quote-style = "double" +indent-style = "space" +skip-magic-trailing-comma = false +line-ending = "auto" -[tool.coverage.run] -omit = ["roboticstoolbox/examples/*", "test_*.py"] diff --git a/roboticstoolbox/__init__.py b/roboticstoolbox/__init__.py index 2890c4d5a..b68912d4e 100644 --- a/roboticstoolbox/__init__.py +++ b/roboticstoolbox/__init__.py @@ -98,6 +98,7 @@ try: import importlib.metadata + __version__ = importlib.metadata.version("roboticstoolbox-python") except: pass diff --git a/roboticstoolbox/backends/Connector.py b/roboticstoolbox/backends/Connector.py index 829e4a8c7..4bcc1f890 100644 --- a/roboticstoolbox/backends/Connector.py +++ b/roboticstoolbox/backends/Connector.py @@ -7,7 +7,6 @@ class Connector(ABC): - def __init__(self): super().__init__() @@ -17,17 +16,17 @@ def __init__(self): @abstractmethod def launch(self): - ''' + """ launch(args) launch the external program with an empty or specific scene as defined by args - ''' + """ pass @abstractmethod def step(self): - ''' + """ step(dt) triggers the external program to make a time step of defined time updating the state of the environment as defined by the robot's actions. @@ -39,36 +38,36 @@ def step(self): by the robot object, and not all robot objects support all control types. - ''' + """ pass @abstractmethod def reset(self): - ''' + """ reset() triggers the external program to reset to the original state defined by launch - ''' + """ pass @abstractmethod def restart(self): - ''' + """ restart() triggers the external program to close and relaunch to thestate defined by launch - ''' + """ pass @abstractmethod def close(self): - ''' + """ close() triggers the external program to gracefully close - ''' + """ pass @@ -78,31 +77,31 @@ def close(self): @abstractmethod def add(self): - ''' + """ id = add(object) adds the object to the external environment. object must be of an appropriate class. This adds a object object to a list of objects which will act upon the step() method being called. - ''' + """ pass @abstractmethod def remove(self): - ''' + """ remove(id) removes the object from the external environment. - ''' + """ pass @abstractmethod - def hold(self): # pragma nocover - ''' + def hold(self): # pragma nocover + """ hold() keeps the backend open i.e. stops the program from closing once the main script has finished. This method may need keep an even loop running for the backend to keep it responsive. - ''' + """ pass diff --git a/roboticstoolbox/backends/Dynamixel/dynamixel_io.py b/roboticstoolbox/backends/Dynamixel/dynamixel_io.py index afc137de5..8daccc99c 100644 --- a/roboticstoolbox/backends/Dynamixel/dynamixel_io.py +++ b/roboticstoolbox/backends/Dynamixel/dynamixel_io.py @@ -3,7 +3,7 @@ import time from pathlib import Path -import dynamixel_sdk as sdk # Uses Dynamixel SDK library +import dynamixel_sdk as sdk # Uses Dynamixel SDK library # list of all models https://emanual.robotis.com/docs/en/software/dynamixel/dynamixel_workbench/ @@ -12,10 +12,9 @@ # XM, XH better control, XH has Maxon motor import json -class DynamixelString: +class DynamixelString: def __init__(self, baudrate=1000000, port=None, protocol=1, top=256, verbose=True): - jsonfile = Path(__file__).parent / "dynamixel.json" print(jsonfile) # load the JSON file and convert keys from strings to ints @@ -56,35 +55,34 @@ def __init__(self, baudrate=1000000, port=None, protocol=1, top=256, verbose=Tru # X-series memory map self.address = {} - self.address['model'] = (2, 4) - self.address['firmware'] = (6, 1) - self.address['return_delay'] = (9, 1) - self.address['drivemode'] = (10, 1, self._drivemode_handler) - self.address['opmode'] = (11, 1, self._opmode_handler) - self.address['shadow_id'] = (12, 1) + self.address["model"] = (2, 4) + self.address["firmware"] = (6, 1) + self.address["return_delay"] = (9, 1) + self.address["drivemode"] = (10, 1, self._drivemode_handler) + self.address["opmode"] = (11, 1, self._opmode_handler) + self.address["shadow_id"] = (12, 1) - self.address['min_position'] = (52, 4) - self.address['max_position'] = (48, 4) - self.address['velocity_limit'] = (44, 4) + self.address["min_position"] = (52, 4) + self.address["max_position"] = (48, 4) + self.address["velocity_limit"] = (44, 4) - self.address['torque_enable'] = (64, 1) - self.address['hardware_error_status'] = (70, 1) + self.address["torque_enable"] = (64, 1) + self.address["hardware_error_status"] = (70, 1) - self.address['goal_position'] = (116, 4) - self.address['goal_velocity'] = (104, 4) - self.address['goal_pwm'] = (100, 2) + self.address["goal_position"] = (116, 4) + self.address["goal_velocity"] = (104, 4) + self.address["goal_pwm"] = (100, 2) - self.address['present_position'] = (132, 4) - self.address['present_position2'] = (132, 2) - self.address['present_current'] = (126, 2) - self.address['present_velocity'] = (128, 2) + self.address["present_position"] = (132, 4) + self.address["present_position2"] = (132, 2) + self.address["present_current"] = (126, 2) + self.address["present_velocity"] = (128, 2) - self.address['profile_velocity'] = (112, 4) - self.address['profile_acceleration'] = (108, 4) + self.address["profile_velocity"] = (112, 4) + self.address["profile_acceleration"] = (108, 4) - self.address['temp'] = (146, 1) - self.address['led'] = (65, 1) - + self.address["temp"] = (146, 1) + self.address["led"] = (65, 1) self.idlist = [] self.models = {} @@ -95,13 +93,13 @@ def __init__(self, baudrate=1000000, port=None, protocol=1, top=256, verbose=Tru if model > 0: self.idlist.append(id) self.models[id] = model - self.set(id, 'torque_enable', False) - self.set(id, 'led', False) - print('servos found', self.idlist) + self.set(id, "torque_enable", False) + self.set(id, "led", False) + print("servos found", self.idlist) - error = self.get('all', 'hardware_error_status') + error = self.get("all", "hardware_error_status") if any(error): - print('hardware error', error) + print("hardware error", error) def close(self): self.port.closePort() @@ -117,110 +115,111 @@ def ping(self, id): def modelname(self, id): mn = self.models[id] try: - return self.modeldict[mn]['model'] + return self.modeldict[mn]["model"] except KeyError: return "?? mn={}".format(mn) def list(self): for id in self.idlist: - fw = self.get(id, 'firmware') + fw = self.get(id, "firmware") print("{:2d} {:3d} {:s}".format(id, fw, self.modelname(id))) - def enable(self, on, id='all'): + def enable(self, on, id="all"): # enable(True) # all enabled # enable (True, 1) # 1 is enabled # enable(True, [1, 2]) # 1, 2 are enabled # enable([True, False], [1, 2]) # 1 is enabled, 2 is disabled # enable([True, True, False, False]) # 1,2 enabled, 3,4 disabled - if id == 'all': + if id == "all": id = self.idlist if isinstance(id, list): if isinstance(on, bool): for id in self.idlist: - self.register_write(id, 'torque_enable', on) + self.register_write(id, "torque_enable", on) else: - assert len(on) == len(id), 'incorrect enable flags' + assert len(on) == len(id), "incorrect enable flags" for i in id: - assert id in self.idlist, 'invalid id' - self.register_write(id, 'torque_enable', on[i]) + assert id in self.idlist, "invalid id" + self.register_write(id, "torque_enable", on[i]) else: - assert id in self.idlist, 'invalid id' - self.register_write(id, 'torque_enable', on) - + assert id in self.idlist, "invalid id" + self.register_write(id, "torque_enable", on) def ismoving(self, id): return 0 - + def setposition(self, id, pos): if id is None: # TODO allow numpy array, use argcheck.getvector - assert isinstance(pos, (int, float)) or instance(pos, list) and len(pos) == self.n, 'incorrect position value' + assert ( + isinstance(pos, (int, float)) + or instance(pos, list) + and len(pos) == self.n + ), "incorrect position value" for id in self.idlist: - self.register_write(id, 'goal_position', pos[i]) + self.register_write(id, "goal_position", pos[i]) else: - assert id in self.idlist, 'invalid id' - self.register_write(id, 'goal_position', pos) - - - def getposition(self, id='all'): - return self._getvalue('present_position', id) + assert id in self.idlist, "invalid id" + self.register_write(id, "goal_position", pos) + def getposition(self, id="all"): + return self._getvalue("present_position", id) def _opmode_handler(self, op, id, register, value=None): - dict = {1: "velocity", 3: "position", 4:"xposition", 16:"pwm"} + dict = {1: "velocity", 3: "position", 4: "xposition", 16: "pwm"} - if op == 'get': + if op == "get": x = self.register_read(id, register) return dict[x] - elif op == 'set': + elif op == "set": for k, v in dict.items(): if v == value: - self.register_write(id, register, k) + self.register_write(id, register, k) # for setting: handler(self, 'set', id, register, newvalue) -> value to write to register # for getting: handler(self, 'get', id, register) -> value to return def _drivemode_handler(self, op, id, register, value=None): - if op == 'get': + if op == "get": x = self.register_read(id, register) r = [] if (x & 1) == 1: - r.append('reverse') + r.append("reverse") elif (x & 1) == 0: - r.append('forward') + r.append("forward") elif (x & 4) == 4: - r.append('tprofile') + r.append("tprofile") elif (x & 4) == 0: - r.append('vprofile') + r.append("vprofile") return r - elif op == 'set': + elif op == "set": curval = self.register_read(id, register) - if value == 'reverse': + if value == "reverse": curval |= 1 - elif value == 'forward': + elif value == "forward": curval &= ~1 - elif value == 'tprofile': + elif value == "tprofile": curval |= 4 - elif value == 'vprofile': + elif value == "vprofile": curval &= ~4 self.register_write(id, register, curval) # simple i/o using separate servo transations def get(self, id, register): if len(self.address[register]) > 2: - reader = lambda id, register: self.address[register][2]('get', id, register) + reader = lambda id, register: self.address[register][2]("get", id, register) else: reader = self.register_read - if id == 'all': + if id == "all": id = self.idlist if isinstance(id, list): value = [] for i in id: - assert i in self.idlist, 'invalid id' + assert i in self.idlist, "invalid id" value.append(reader(i, register)) return value else: - assert id in self.idlist, 'invalid id' + assert id in self.idlist, "invalid id" return reader(id, register) def set(self, id, register, value): @@ -228,60 +227,87 @@ def set(self, id, register, value): print(f"set id={id}; {register} := {value}") if len(self.address[register]) > 2: - writer = lambda id, register, value: self.address[register][2]('set', id, register, value) + writer = lambda id, register, value: self.address[register][2]( + "set", id, register, value + ) else: writer = self.register_write - if id == 'all': + if id == "all": id = self.idlist if isinstance(id, list): if isinstance(value, list): - assert len(value) == len(id), 'length of value list must match number of servos' + assert len(value) == len( + id + ), "length of value list must match number of servos" for i, v in zip(id, value): - assert i in self.idlist, 'invalid id' + assert i in self.idlist, "invalid id" writer(i, register, v) else: for i in id: writer(i, register, value) else: - assert id in self.idlist, 'invalid id %d' % (id,) + assert id in self.idlist, "invalid id %d" % (id,) writer(id, register, value) - + # wrapper on the Dynamixel SDK def register_read(self, id, regname): address, nbytes = self.address[regname][0:2] if nbytes == 1: - dxl_value, dxl_comm_result, dxl_error = self.packetHandler.read1ByteTxRx(self.port, id, address) + dxl_value, dxl_comm_result, dxl_error = self.packetHandler.read1ByteTxRx( + self.port, id, address + ) elif nbytes == 2: - dxl_value, dxl_comm_result, dxl_error = self.packetHandler.read2ByteTxRx(self.port, id, address) + dxl_value, dxl_comm_result, dxl_error = self.packetHandler.read2ByteTxRx( + self.port, id, address + ) elif nbytes == 4: - dxl_value, dxl_comm_result, dxl_error = self.packetHandler.read4ByteTxRx(self.port, id, address) + dxl_value, dxl_comm_result, dxl_error = self.packetHandler.read4ByteTxRx( + self.port, id, address + ) if dxl_comm_result != sdk.COMM_SUCCESS: - raise RuntimeError(self.packetHandler.getTxRxResult(dxl_comm_result) + " id={}, register={}".format(id, regname)) + raise RuntimeError( + self.packetHandler.getTxRxResult(dxl_comm_result) + + " id={}, register={}".format(id, regname) + ) elif dxl_error != 0: - raise RuntimeError(self.packetHandler.getRxPacketError(dxl_error) + " id={}, register={}".format(id, regname)) + raise RuntimeError( + self.packetHandler.getRxPacketError(dxl_error) + + " id={}, register={}".format(id, regname) + ) return dxl_value - def register_write(self, id, regname, value): - #print('register_write', id, regname, value) + # print('register_write', id, regname, value) if isinstance(regname, str): address, nbytes = self.address[regname][0:2] elif isinstance(regname, tuple) and len(regname) == 2: address, nbytes = regname else: - raise ValueError('register must be a string or (address, length) tuple') + raise ValueError("register must be a string or (address, length) tuple") if nbytes == 1: - dxl_comm_result, dxl_error = self.packetHandler.write1ByteTxRx(self.port, id, address, value) + dxl_comm_result, dxl_error = self.packetHandler.write1ByteTxRx( + self.port, id, address, value + ) elif nbytes == 2: - dxl_comm_result, dxl_error = self.packetHandler.write2ByteTxRx(self.port, id, address, value) + dxl_comm_result, dxl_error = self.packetHandler.write2ByteTxRx( + self.port, id, address, value + ) elif nbytes == 4: - dxl_comm_result, dxl_error = self.packetHandler.write4ByteTxRx(self.port, id, address, value) + dxl_comm_result, dxl_error = self.packetHandler.write4ByteTxRx( + self.port, id, address, value + ) if dxl_comm_result != sdk.COMM_SUCCESS: - raise RuntimeError(self.packetHandler.getTxRxResult(dxl_comm_result) + " id={}, register={}".format(id, regname)) + raise RuntimeError( + self.packetHandler.getTxRxResult(dxl_comm_result) + + " id={}, register={}".format(id, regname) + ) elif dxl_error != 0: - raise RuntimeError(self.packetHandler.getRxPacketError(dxl_error) + " id={}, register={}".format(id, regname)) + raise RuntimeError( + self.packetHandler.getRxPacketError(dxl_error) + + " id={}, register={}".format(id, regname) + ) def register_indirect_config(self, id, indirectbase, regnames): iaddr = (indirectbase - 1) * 2 + 168 @@ -290,8 +316,8 @@ def register_indirect_config(self, id, indirectbase, regnames): for regname in regnames: address, nbytes = self.address[regname] for i in range(0, nbytes): - self.register_write(id, (iaddr+i*2, 2), address+i) - print(id, address+i, ' --> ', iaddr+i*2) + self.register_write(id, (iaddr + i * 2, 2), address + i) + print(id, address + i, " --> ", iaddr + i * 2) iaddr += 2 * nbytes count += nbytes @@ -300,21 +326,23 @@ def register_indirect_config(self, id, indirectbase, regnames): def register_read_sync(self, id, addrlen): # create an indirect table, keep track of how many bytes to be read - groupSyncRead = GroupSyncRead(self.port, self.packetHandler, addrlen[0], addrlen[1]) + groupSyncRead = GroupSyncRead( + self.port, self.packetHandler, addrlen[0], addrlen[1] + ) for id in self.active: dxl_addparam_result = groupSyncRead.addParam(id) if dxl_addparam_result != True: - pass # bad thing + pass # bad thing def pulseleds(self, nblinks=4, dt=0.1): for i in range(0, nblinks): - self.set('all', 'led', False) + self.set("all", "led", False) time.sleep(dt) - self.set('all', 'led', True) + self.set("all", "led", True) time.sleep(dt) -class DynRobot: +class DynRobot: def __init__(self, dynamixels, arm, gripper): self.dynamixels = dynamixels self.idlist = [] @@ -328,30 +356,32 @@ def __init__(self, dynamixels, arm, gripper): if motor[1] < 0: # motor is reveresed second = -motor[1] - self.dynamixels.set(second, 'opmode', 'reverse') + self.dynamixels.set(second, "opmode", "reverse") else: second = motor[1] # second motor shadows the first - self.dynamixels.set(second, 'shadow_id', motor[0]) + self.dynamixels.set(second, "shadow_id", motor[0]) - self.q0 = self.dynamixels.get(self.idlist, 'present_position') + self.q0 = self.dynamixels.get(self.idlist, "present_position") class SyncIO: pass def syncread_config(self, regnames, indirectbase=1): - sr = SyncIO() # lgtm [py/unused-local-variable] + sr = SyncIO() # lgtm [py/unused-local-variable] for id in self.idlist: - (a, l) = d.register_indirect_config(id, indirectbase, regnames) # lgtm [py/unused-local-variable] + (a, l) = d.register_indirect_config( + id, indirectbase, regnames + ) # lgtm [py/unused-local-variable] def setmode(self, m): - self.dynamixels.set(self.idlist, 'drivemode', m) + self.dynamixels.set(self.idlist, "drivemode", m) def moveto(self, q, t=2000, tacc=200, block=True): - self.dynamixels.set(self.idlist, 'profile_velocity', int(t * 1000)) - self.dynamixels.set(self.idlist, 'profile_acceleration', int(tacc * 1000)) - self.dynamixels.set(self.idlist, 'goal_position', q) + self.dynamixels.set(self.idlist, "profile_velocity", int(t * 1000)) + self.dynamixels.set(self.idlist, "profile_acceleration", int(tacc * 1000)) + self.dynamixels.set(self.idlist, "goal_position", q) def getpos(self): pass @@ -362,30 +392,39 @@ def syncwrite_config(self): def enable(self, on): if on: self.dynamixels.pulseleds() - self.dynamixels.set(self.idlist, 'torque_enable', on) + self.dynamixels.set(self.idlist, "torque_enable", on) if not on: - self.dynamixels.set('all', 'led', False) + self.dynamixels.set("all", "led", False) -if __name__ == "__main__": +if __name__ == "__main__": import argparse - parser = argparse.ArgumentParser(description='Dynamixel interactive console') - parser.add_argument('--port', '-p', default="/dev/tty.usbserial-FT4NQ6ZP", - help='specify name of serial port') - parser.add_argument('--baudrate', '-b', type=int, default=1000000, - help='serial baud rate') - parser.add_argument('--protocol', type=int, default=2, - help='Dynamixel protocol level') - parser.add_argument('--scan', type=int, default=20, - help='Scan for Dynamixel ids 1 to this number inclusive') + parser = argparse.ArgumentParser(description="Dynamixel interactive console") + parser.add_argument( + "--port", + "-p", + default="/dev/tty.usbserial-FT4NQ6ZP", + help="specify name of serial port", + ) + parser.add_argument( + "--baudrate", "-b", type=int, default=1000000, help="serial baud rate" + ) + parser.add_argument( + "--protocol", type=int, default=2, help="Dynamixel protocol level" + ) + parser.add_argument( + "--scan", + type=int, + default=20, + help="Scan for Dynamixel ids 1 to this number inclusive", + ) args = parser.parse_args() - dms = DynamixelString(port=args.port, - baudrate=args.baudrate, - protocol=args.protocol, - top=args.scan) - dms.set('all', 'return_delay', 0) + dms = DynamixelString( + port=args.port, baudrate=args.baudrate, protocol=args.protocol, top=args.scan + ) + dms.set("all", "return_delay", 0) # simple console-based command handler while True: diff --git a/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/__init__.py b/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/__init__.py index 58d1fcbea..0711d2fb9 100644 --- a/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/__init__.py +++ b/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/__init__.py @@ -19,9 +19,9 @@ # Author: Ryu Woon Jung (Leon) -from .port_handler import * # lgtm [py/polluting-import] -from .packet_handler import * # lgtm [py/polluting-import] -from .group_sync_read import * # lgtm [py/polluting-import] -from .group_sync_write import * # lgtm [py/polluting-import] -from .group_bulk_read import * # lgtm [py/polluting-import] -from .group_bulk_write import * # lgtm [py/polluting-import] +from .port_handler import * # lgtm [py/polluting-import] +from .packet_handler import * # lgtm [py/polluting-import] +from .group_sync_read import * # lgtm [py/polluting-import] +from .group_sync_write import * # lgtm [py/polluting-import] +from .group_bulk_read import * # lgtm [py/polluting-import] +from .group_bulk_write import * # lgtm [py/polluting-import] diff --git a/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/group_bulk_read.py b/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/group_bulk_read.py index 087ee42f9..2332cabd6 100644 --- a/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/group_bulk_read.py +++ b/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/group_bulk_read.py @@ -19,7 +19,7 @@ # Author: Ryu Woon Jung (Leon) -from .robotis_def import * # lgtm [py/polluting-import] +from .robotis_def import * # lgtm [py/polluting-import] PARAM_NUM_DATA = 0 PARAM_NUM_ADDRESS = 1 @@ -86,9 +86,13 @@ def txPacket(self): self.makeParam() if self.ph.getProtocolVersion() == 1.0: - return self.ph.bulkReadTx(self.port, self.param, len(self.data_dict.keys()) * 3) + return self.ph.bulkReadTx( + self.port, self.param, len(self.data_dict.keys()) * 3 + ) else: - return self.ph.bulkReadTx(self.port, self.param, len(self.data_dict.keys()) * 5) + return self.ph.bulkReadTx( + self.port, self.param, len(self.data_dict.keys()) * 5 + ) def rxPacket(self): self.last_result = False @@ -99,8 +103,9 @@ def rxPacket(self): return COMM_NOT_AVAILABLE for dxl_id in self.data_dict: - self.data_dict[dxl_id][PARAM_NUM_DATA], result, _ = self.ph.readRx(self.port, dxl_id, - self.data_dict[dxl_id][PARAM_NUM_LENGTH]) + self.data_dict[dxl_id][PARAM_NUM_DATA], result, _ = self.ph.readRx( + self.port, dxl_id, self.data_dict[dxl_id][PARAM_NUM_LENGTH] + ) if result != COMM_SUCCESS: return result @@ -122,7 +127,10 @@ def isAvailable(self, dxl_id, address, data_length): start_addr = self.data_dict[dxl_id][PARAM_NUM_ADDRESS] - if (address < start_addr) or (start_addr + self.data_dict[dxl_id][PARAM_NUM_LENGTH] - data_length < address): + if (address < start_addr) or ( + start_addr + self.data_dict[dxl_id][PARAM_NUM_LENGTH] - data_length + < address + ): return False return True @@ -136,12 +144,20 @@ def getData(self, dxl_id, address, data_length): if data_length == 1: return self.data_dict[dxl_id][PARAM_NUM_DATA][address - start_addr] elif data_length == 2: - return DXL_MAKEWORD(self.data_dict[dxl_id][PARAM_NUM_DATA][address - start_addr], - self.data_dict[dxl_id][PARAM_NUM_DATA][address - start_addr + 1]) + return DXL_MAKEWORD( + self.data_dict[dxl_id][PARAM_NUM_DATA][address - start_addr], + self.data_dict[dxl_id][PARAM_NUM_DATA][address - start_addr + 1], + ) elif data_length == 4: - return DXL_MAKEDWORD(DXL_MAKEWORD(self.data_dict[dxl_id][PARAM_NUM_DATA][address - start_addr + 0], - self.data_dict[dxl_id][PARAM_NUM_DATA][address - start_addr + 1]), - DXL_MAKEWORD(self.data_dict[dxl_id][PARAM_NUM_DATA][address - start_addr + 2], - self.data_dict[dxl_id][PARAM_NUM_DATA][address - start_addr + 3])) + return DXL_MAKEDWORD( + DXL_MAKEWORD( + self.data_dict[dxl_id][PARAM_NUM_DATA][address - start_addr + 0], + self.data_dict[dxl_id][PARAM_NUM_DATA][address - start_addr + 1], + ), + DXL_MAKEWORD( + self.data_dict[dxl_id][PARAM_NUM_DATA][address - start_addr + 2], + self.data_dict[dxl_id][PARAM_NUM_DATA][address - start_addr + 3], + ), + ) else: return 0 diff --git a/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/group_bulk_write.py b/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/group_bulk_write.py index 8296bfdf6..595f2f8cc 100644 --- a/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/group_bulk_write.py +++ b/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/group_bulk_write.py @@ -19,7 +19,7 @@ # Author: Ryu Woon Jung (Leon) -from .robotis_def import * # lgtm [py/polluting-import] +from .robotis_def import * # lgtm [py/polluting-import] class GroupBulkWrite: diff --git a/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/group_sync_read.py b/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/group_sync_read.py index 3126eb2ea..1d1297083 100644 --- a/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/group_sync_read.py +++ b/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/group_sync_read.py @@ -19,7 +19,7 @@ # Author: Ryu Woon Jung (Leon) -from .robotis_def import * # lgtm [py/polluting-import] +from .robotis_def import * # lgtm [py/polluting-import] class GroupSyncRead: @@ -84,8 +84,13 @@ def txPacket(self): if self.is_param_changed is True or not self.param: self.makeParam() - return self.ph.syncReadTx(self.port, self.start_address, self.data_length, self.param, - len(self.data_dict.keys()) * 1) + return self.ph.syncReadTx( + self.port, + self.start_address, + self.data_length, + self.param, + len(self.data_dict.keys()) * 1, + ) def rxPacket(self): self.last_result = False @@ -99,7 +104,9 @@ def rxPacket(self): return COMM_NOT_AVAILABLE for dxl_id in self.data_dict: - self.data_dict[dxl_id], result, _ = self.ph.readRx(self.port, dxl_id, self.data_length) + self.data_dict[dxl_id], result, _ = self.ph.readRx( + self.port, dxl_id, self.data_length + ) if result != COMM_SUCCESS: return result @@ -119,10 +126,16 @@ def txRxPacket(self): return self.rxPacket() def isAvailable(self, dxl_id, address, data_length): - if self.ph.getProtocolVersion() == 1.0 or self.last_result is False or dxl_id not in self.data_dict: + if ( + self.ph.getProtocolVersion() == 1.0 + or self.last_result is False + or dxl_id not in self.data_dict + ): return False - if (address < self.start_address) or (self.start_address + self.data_length - data_length < address): + if (address < self.start_address) or ( + self.start_address + self.data_length - data_length < address + ): return False return True @@ -134,12 +147,20 @@ def getData(self, dxl_id, address, data_length): if data_length == 1: return self.data_dict[dxl_id][address - self.start_address] elif data_length == 2: - return DXL_MAKEWORD(self.data_dict[dxl_id][address - self.start_address], - self.data_dict[dxl_id][address - self.start_address + 1]) + return DXL_MAKEWORD( + self.data_dict[dxl_id][address - self.start_address], + self.data_dict[dxl_id][address - self.start_address + 1], + ) elif data_length == 4: - return DXL_MAKEDWORD(DXL_MAKEWORD(self.data_dict[dxl_id][address - self.start_address + 0], - self.data_dict[dxl_id][address - self.start_address + 1]), - DXL_MAKEWORD(self.data_dict[dxl_id][address - self.start_address + 2], - self.data_dict[dxl_id][address - self.start_address + 3])) + return DXL_MAKEDWORD( + DXL_MAKEWORD( + self.data_dict[dxl_id][address - self.start_address + 0], + self.data_dict[dxl_id][address - self.start_address + 1], + ), + DXL_MAKEWORD( + self.data_dict[dxl_id][address - self.start_address + 2], + self.data_dict[dxl_id][address - self.start_address + 3], + ), + ) else: return 0 diff --git a/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/group_sync_write.py b/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/group_sync_write.py index 3c3468efd..2a91e4da5 100644 --- a/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/group_sync_write.py +++ b/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/group_sync_write.py @@ -19,7 +19,7 @@ # Author: Ryu Woon Jung (Leon) -from .robotis_def import * # lgtm [py/polluting-import] +from .robotis_def import * # lgtm [py/polluting-import] class GroupSyncWrite: @@ -90,5 +90,10 @@ def txPacket(self): if self.is_param_changed is True or not self.param: self.makeParam() - return self.ph.syncWriteTxOnly(self.port, self.start_address, self.data_length, self.param, - len(self.data_dict.keys()) * (1 + self.data_length)) + return self.ph.syncWriteTxOnly( + self.port, + self.start_address, + self.data_length, + self.param, + len(self.data_dict.keys()) * (1 + self.data_length), + ) diff --git a/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/packet_handler.py b/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/packet_handler.py index c51249d45..61155b4f5 100644 --- a/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/packet_handler.py +++ b/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/packet_handler.py @@ -19,8 +19,8 @@ # Author: Ryu Woon Jung (Leon) -from .protocol1_packet_handler import * # lgtm [py/polluting-import] -from .protocol2_packet_handler import * # lgtm [py/polluting-import] +from .protocol1_packet_handler import * # lgtm [py/polluting-import] +from .protocol2_packet_handler import * # lgtm [py/polluting-import] def PacketHandler(protocol_version): diff --git a/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/port_handler.py b/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/port_handler.py index ed302f058..191b3d848 100644 --- a/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/port_handler.py +++ b/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/port_handler.py @@ -22,7 +22,6 @@ import time import serial import sys -import platform # lgtm [py/unused-import] LATENCY_TIMER = 16 DEFAULT_BAUDRATE = 1000000 @@ -74,7 +73,7 @@ def getBytesAvailable(self): return self.ser.in_waiting def readPort(self, length): - if (sys.version_info > (3, 0)): + if sys.version_info > (3, 0): return self.ser.read(length) else: return [ord(ch) for ch in self.ser.read(length)] @@ -84,7 +83,9 @@ def writePort(self, packet): def setPacketTimeout(self, packet_length): self.packet_start_time = self.getCurrentTime() - self.packet_timeout = (self.tx_time_per_byte * packet_length) + (LATENCY_TIMER * 2.0) + 2.0 + self.packet_timeout = ( + (self.tx_time_per_byte * packet_length) + (LATENCY_TIMER * 2.0) + 2.0 + ) def setPacketTimeoutMillis(self, msec): self.packet_start_time = self.getCurrentTime() @@ -117,7 +118,7 @@ def setupPort(self, cflag_baud): # parity = serial.PARITY_ODD, # stopbits = serial.STOPBITS_TWO, bytesize=serial.EIGHTBITS, - timeout=0 + timeout=0, ) self.is_open = True @@ -129,8 +130,25 @@ def setupPort(self, cflag_baud): return True def getCFlagBaud(self, baudrate): - if baudrate in [9600, 19200, 38400, 57600, 115200, 230400, 460800, 500000, 576000, 921600, 1000000, 1152000, - 2000000, 2500000, 3000000, 3500000, 4000000]: + if baudrate in [ + 9600, + 19200, + 38400, + 57600, + 115200, + 230400, + 460800, + 500000, + 576000, + 921600, + 1000000, + 1152000, + 2000000, + 2500000, + 3000000, + 3500000, + 4000000, + ]: return baudrate else: - return -1 + return -1 diff --git a/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/protocol1_packet_handler.py b/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/protocol1_packet_handler.py index b1b2a92fb..2ce6f6be1 100644 --- a/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/protocol1_packet_handler.py +++ b/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/protocol1_packet_handler.py @@ -19,7 +19,7 @@ # Author: Ryu Woon Jung (Leon) -from .robotis_def import * # lgtm [py/polluting-import] +from .robotis_def import * # lgtm [py/polluting-import] TXPACKET_MAX_LEN = 250 RXPACKET_MAX_LEN = 250 @@ -116,7 +116,7 @@ def txPacket(self, port, txpacket): txpacket[total_packet_length - 1] = ~checksum & 0xFF - #print "[TxPacket] %r" % txpacket + # print "[TxPacket] %r" % txpacket # tx packet port.clearPort() @@ -130,7 +130,7 @@ def txPacket(self, port, txpacket): def rxPacket(self, port): rxpacket = [] - result = COMM_TX_FAIL # lgtm [py/multiple-definition] + result = COMM_TX_FAIL # lgtm [py/multiple-definition] checksum = 0 rx_length = 0 wait_length = 6 # minimum length (HEADER0 HEADER1 ID LENGTH ERROR CHKSUM) @@ -145,8 +145,11 @@ def rxPacket(self, port): break if idx == 0: # found at the beginning of the packet - if (rxpacket[PKT_ID] > 0xFD) or (rxpacket[PKT_LENGTH] > RXPACKET_MAX_LEN) or ( - rxpacket[PKT_ERROR] > 0x7F): + if ( + (rxpacket[PKT_ID] > 0xFD) + or (rxpacket[PKT_LENGTH] > RXPACKET_MAX_LEN) + or (rxpacket[PKT_ERROR] > 0x7F) + ): # unavailable ID or unavailable Length or unavailable Error # remove the first byte in the packet del rxpacket[0] @@ -183,7 +186,7 @@ def rxPacket(self, port): else: # remove unnecessary packets - del rxpacket[0: idx] + del rxpacket[0:idx] rx_length -= idx else: @@ -197,7 +200,7 @@ def rxPacket(self, port): port.is_using = False - #print "[RxPacket] %r" % rxpacket + # print "[RxPacket] %r" % rxpacket return rxpacket, result @@ -216,7 +219,7 @@ def txRxPacket(self, port, txpacket): result = COMM_NOT_AVAILABLE # (ID == Broadcast ID) == no need to wait for status packet or not available - if (txpacket[PKT_ID] == BROADCAST_ID): + if txpacket[PKT_ID] == BROADCAST_ID: port.is_using = False return rxpacket, result, error @@ -253,7 +256,9 @@ def ping(self, port, dxl_id): rxpacket, result, error = self.txRxPacket(port, txpacket) if result == COMM_SUCCESS: - data_read, result, error = self.readTxRx(port, dxl_id, 0, 2) # Address 0 : Model Number + data_read, result, error = self.readTxRx( + port, dxl_id, 0, 2 + ) # Address 0 : Model Number if result == COMM_SUCCESS: model_number = DXL_MAKEWORD(data_read[0], data_read[1]) @@ -289,7 +294,6 @@ def factoryReset(self, port, dxl_id): return result, error def readTx(self, port, dxl_id, address, length): - txpacket = [0] * 8 if dxl_id >= BROADCAST_ID: @@ -310,7 +314,7 @@ def readTx(self, port, dxl_id, address, length): return result def readRx(self, port, dxl_id, length): - result = COMM_TX_FAIL # lgtm [py/multiple-definition] + result = COMM_TX_FAIL # lgtm [py/multiple-definition] error = 0 rxpacket = None @@ -325,7 +329,7 @@ def readRx(self, port, dxl_id, length): if result == COMM_SUCCESS and rxpacket[PKT_ID] == dxl_id: error = rxpacket[PKT_ERROR] - data.extend(rxpacket[PKT_PARAMETER0: PKT_PARAMETER0 + length]) + data.extend(rxpacket[PKT_PARAMETER0 : PKT_PARAMETER0 + length]) return data, result, error @@ -346,7 +350,7 @@ def readTxRx(self, port, dxl_id, address, length): if result == COMM_SUCCESS: error = rxpacket[PKT_ERROR] - data.extend(rxpacket[PKT_PARAMETER0: PKT_PARAMETER0 + length]) + data.extend(rxpacket[PKT_PARAMETER0 : PKT_PARAMETER0 + length]) return data, result, error @@ -381,14 +385,24 @@ def read4ByteTx(self, port, dxl_id, address): def read4ByteRx(self, port, dxl_id): data, result, error = self.readRx(port, dxl_id, 4) - data_read = DXL_MAKEDWORD(DXL_MAKEWORD(data[0], data[1]), - DXL_MAKEWORD(data[2], data[3])) if (result == COMM_SUCCESS) else 0 + data_read = ( + DXL_MAKEDWORD( + DXL_MAKEWORD(data[0], data[1]), DXL_MAKEWORD(data[2], data[3]) + ) + if (result == COMM_SUCCESS) + else 0 + ) return data_read, result, error def read4ByteTxRx(self, port, dxl_id, address): data, result, error = self.readTxRx(port, dxl_id, address, 4) - data_read = DXL_MAKEDWORD(DXL_MAKEWORD(data[0], data[1]), - DXL_MAKEWORD(data[2], data[3])) if (result == COMM_SUCCESS) else 0 + data_read = ( + DXL_MAKEDWORD( + DXL_MAKEWORD(data[0], data[1]), DXL_MAKEWORD(data[2], data[3]) + ) + if (result == COMM_SUCCESS) + else 0 + ) return data_read, result, error def writeTxOnly(self, port, dxl_id, address, length, data): @@ -399,7 +413,7 @@ def writeTxOnly(self, port, dxl_id, address, length, data): txpacket[PKT_INSTRUCTION] = INST_WRITE txpacket[PKT_PARAMETER0] = address - txpacket[PKT_PARAMETER0 + 1: PKT_PARAMETER0 + 1 + length] = data[0: length] + txpacket[PKT_PARAMETER0 + 1 : PKT_PARAMETER0 + 1 + length] = data[0:length] result = self.txPacket(port, txpacket) port.is_using = False @@ -414,7 +428,7 @@ def writeTxRx(self, port, dxl_id, address, length, data): txpacket[PKT_INSTRUCTION] = INST_WRITE txpacket[PKT_PARAMETER0] = address - txpacket[PKT_PARAMETER0 + 1: PKT_PARAMETER0 + 1 + length] = data[0: length] + txpacket[PKT_PARAMETER0 + 1 : PKT_PARAMETER0 + 1 + length] = data[0:length] rxpacket, result, error = self.txRxPacket(port, txpacket) return result, error @@ -436,17 +450,21 @@ def write2ByteTxRx(self, port, dxl_id, address, data): return self.writeTxRx(port, dxl_id, address, 2, data_write) def write4ByteTxOnly(self, port, dxl_id, address, data): - data_write = [DXL_LOBYTE(DXL_LOWORD(data)), - DXL_HIBYTE(DXL_LOWORD(data)), - DXL_LOBYTE(DXL_HIWORD(data)), - DXL_HIBYTE(DXL_HIWORD(data))] + data_write = [ + DXL_LOBYTE(DXL_LOWORD(data)), + DXL_HIBYTE(DXL_LOWORD(data)), + DXL_LOBYTE(DXL_HIWORD(data)), + DXL_HIBYTE(DXL_HIWORD(data)), + ] return self.writeTxOnly(port, dxl_id, address, 4, data_write) def write4ByteTxRx(self, port, dxl_id, address, data): - data_write = [DXL_LOBYTE(DXL_LOWORD(data)), - DXL_HIBYTE(DXL_LOWORD(data)), - DXL_LOBYTE(DXL_HIWORD(data)), - DXL_HIBYTE(DXL_HIWORD(data))] + data_write = [ + DXL_LOBYTE(DXL_LOWORD(data)), + DXL_HIBYTE(DXL_LOWORD(data)), + DXL_LOBYTE(DXL_HIWORD(data)), + DXL_HIBYTE(DXL_HIWORD(data)), + ] return self.writeTxRx(port, dxl_id, address, 4, data_write) def regWriteTxOnly(self, port, dxl_id, address, length, data): @@ -457,7 +475,7 @@ def regWriteTxOnly(self, port, dxl_id, address, length, data): txpacket[PKT_INSTRUCTION] = INST_REG_WRITE txpacket[PKT_PARAMETER0] = address - txpacket[PKT_PARAMETER0 + 1: PKT_PARAMETER0 + 1 + length] = data[0: length] + txpacket[PKT_PARAMETER0 + 1 : PKT_PARAMETER0 + 1 + length] = data[0:length] result = self.txPacket(port, txpacket) port.is_using = False @@ -472,7 +490,7 @@ def regWriteTxRx(self, port, dxl_id, address, length, data): txpacket[PKT_INSTRUCTION] = INST_REG_WRITE txpacket[PKT_PARAMETER0] = address - txpacket[PKT_PARAMETER0 + 1: PKT_PARAMETER0 + 1 + length] = data[0: length] + txpacket[PKT_PARAMETER0 + 1 : PKT_PARAMETER0 + 1 + length] = data[0:length] _, result, error = self.txRxPacket(port, txpacket) @@ -486,12 +504,16 @@ def syncWriteTxOnly(self, port, start_address, data_length, param, param_length) # 8: HEADER0 HEADER1 ID LEN INST START_ADDR DATA_LEN ... CHKSUM txpacket[PKT_ID] = BROADCAST_ID - txpacket[PKT_LENGTH] = param_length + 4 # 4: INST START_ADDR DATA_LEN ... CHKSUM + txpacket[PKT_LENGTH] = ( + param_length + 4 + ) # 4: INST START_ADDR DATA_LEN ... CHKSUM txpacket[PKT_INSTRUCTION] = INST_SYNC_WRITE txpacket[PKT_PARAMETER0 + 0] = start_address txpacket[PKT_PARAMETER0 + 1] = data_length - txpacket[PKT_PARAMETER0 + 2: PKT_PARAMETER0 + 2 + param_length] = param[0: param_length] + txpacket[PKT_PARAMETER0 + 2 : PKT_PARAMETER0 + 2 + param_length] = param[ + 0:param_length + ] _, result, _ = self.txRxPacket(port, txpacket) @@ -506,7 +528,9 @@ def bulkReadTx(self, port, param, param_length): txpacket[PKT_INSTRUCTION] = INST_BULK_READ txpacket[PKT_PARAMETER0 + 0] = 0x00 - txpacket[PKT_PARAMETER0 + 1: PKT_PARAMETER0 + 1 + param_length] = param[0: param_length] + txpacket[PKT_PARAMETER0 + 1 : PKT_PARAMETER0 + 1 + param_length] = param[ + 0:param_length + ] result = self.txPacket(port, txpacket) if result == COMM_SUCCESS: diff --git a/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/protocol2_packet_handler.py b/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/protocol2_packet_handler.py index 940dda88c..6e30dc990 100644 --- a/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/protocol2_packet_handler.py +++ b/roboticstoolbox/backends/Dynamixel/dynamixel_sdk/protocol2_packet_handler.py @@ -100,50 +100,272 @@ def getRxPacketError(self, error): return "[RxPacketError] The data value exceeds the limit value!" elif not_alert_error == ERRNUM_ACCESS: - return "[RxPacketError] Writing or Reading is not available to target address!" + return ( + "[RxPacketError] Writing or Reading is not available to target address!" + ) else: return "[RxPacketError] Unknown error code!" def updateCRC(self, crc_accum, data_blk_ptr, data_blk_size): - crc_table = [0x0000, - 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011, - 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, - 0x0022, 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, - 0x8077, 0x0072, 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, - 0x004E, 0x0044, 0x8041, 0x80C3, 0x00C6, 0x00CC, 0x80C9, - 0x00D8, 0x80DD, 0x80D7, 0x00D2, 0x00F0, 0x80F5, 0x80FF, - 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1, 0x00A0, 0x80A5, - 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1, 0x8093, - 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082, - 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, - 0x0192, 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, - 0x01A4, 0x81A1, 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, - 0x01FE, 0x01F4, 0x81F1, 0x81D3, 0x01D6, 0x01DC, 0x81D9, - 0x01C8, 0x81CD, 0x81C7, 0x01C2, 0x0140, 0x8145, 0x814F, - 0x014A, 0x815B, 0x015E, 0x0154, 0x8151, 0x8173, 0x0176, - 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162, 0x8123, - 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132, - 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, - 0x8101, 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, - 0x8317, 0x0312, 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, - 0x032E, 0x0324, 0x8321, 0x0360, 0x8365, 0x836F, 0x036A, - 0x837B, 0x037E, 0x0374, 0x8371, 0x8353, 0x0356, 0x035C, - 0x8359, 0x0348, 0x834D, 0x8347, 0x0342, 0x03C0, 0x83C5, - 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1, 0x83F3, - 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2, - 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, - 0x03B2, 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, - 0x0384, 0x8381, 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, - 0x029E, 0x0294, 0x8291, 0x82B3, 0x02B6, 0x02BC, 0x82B9, - 0x02A8, 0x82AD, 0x82A7, 0x02A2, 0x82E3, 0x02E6, 0x02EC, - 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2, 0x02D0, 0x82D5, - 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1, 0x8243, - 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252, - 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, - 0x8261, 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, - 0x0234, 0x8231, 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, - 0x820D, 0x8207, 0x0202] + crc_table = [ + 0x0000, + 0x8005, + 0x800F, + 0x000A, + 0x801B, + 0x001E, + 0x0014, + 0x8011, + 0x8033, + 0x0036, + 0x003C, + 0x8039, + 0x0028, + 0x802D, + 0x8027, + 0x0022, + 0x8063, + 0x0066, + 0x006C, + 0x8069, + 0x0078, + 0x807D, + 0x8077, + 0x0072, + 0x0050, + 0x8055, + 0x805F, + 0x005A, + 0x804B, + 0x004E, + 0x0044, + 0x8041, + 0x80C3, + 0x00C6, + 0x00CC, + 0x80C9, + 0x00D8, + 0x80DD, + 0x80D7, + 0x00D2, + 0x00F0, + 0x80F5, + 0x80FF, + 0x00FA, + 0x80EB, + 0x00EE, + 0x00E4, + 0x80E1, + 0x00A0, + 0x80A5, + 0x80AF, + 0x00AA, + 0x80BB, + 0x00BE, + 0x00B4, + 0x80B1, + 0x8093, + 0x0096, + 0x009C, + 0x8099, + 0x0088, + 0x808D, + 0x8087, + 0x0082, + 0x8183, + 0x0186, + 0x018C, + 0x8189, + 0x0198, + 0x819D, + 0x8197, + 0x0192, + 0x01B0, + 0x81B5, + 0x81BF, + 0x01BA, + 0x81AB, + 0x01AE, + 0x01A4, + 0x81A1, + 0x01E0, + 0x81E5, + 0x81EF, + 0x01EA, + 0x81FB, + 0x01FE, + 0x01F4, + 0x81F1, + 0x81D3, + 0x01D6, + 0x01DC, + 0x81D9, + 0x01C8, + 0x81CD, + 0x81C7, + 0x01C2, + 0x0140, + 0x8145, + 0x814F, + 0x014A, + 0x815B, + 0x015E, + 0x0154, + 0x8151, + 0x8173, + 0x0176, + 0x017C, + 0x8179, + 0x0168, + 0x816D, + 0x8167, + 0x0162, + 0x8123, + 0x0126, + 0x012C, + 0x8129, + 0x0138, + 0x813D, + 0x8137, + 0x0132, + 0x0110, + 0x8115, + 0x811F, + 0x011A, + 0x810B, + 0x010E, + 0x0104, + 0x8101, + 0x8303, + 0x0306, + 0x030C, + 0x8309, + 0x0318, + 0x831D, + 0x8317, + 0x0312, + 0x0330, + 0x8335, + 0x833F, + 0x033A, + 0x832B, + 0x032E, + 0x0324, + 0x8321, + 0x0360, + 0x8365, + 0x836F, + 0x036A, + 0x837B, + 0x037E, + 0x0374, + 0x8371, + 0x8353, + 0x0356, + 0x035C, + 0x8359, + 0x0348, + 0x834D, + 0x8347, + 0x0342, + 0x03C0, + 0x83C5, + 0x83CF, + 0x03CA, + 0x83DB, + 0x03DE, + 0x03D4, + 0x83D1, + 0x83F3, + 0x03F6, + 0x03FC, + 0x83F9, + 0x03E8, + 0x83ED, + 0x83E7, + 0x03E2, + 0x83A3, + 0x03A6, + 0x03AC, + 0x83A9, + 0x03B8, + 0x83BD, + 0x83B7, + 0x03B2, + 0x0390, + 0x8395, + 0x839F, + 0x039A, + 0x838B, + 0x038E, + 0x0384, + 0x8381, + 0x0280, + 0x8285, + 0x828F, + 0x028A, + 0x829B, + 0x029E, + 0x0294, + 0x8291, + 0x82B3, + 0x02B6, + 0x02BC, + 0x82B9, + 0x02A8, + 0x82AD, + 0x82A7, + 0x02A2, + 0x82E3, + 0x02E6, + 0x02EC, + 0x82E9, + 0x02F8, + 0x82FD, + 0x82F7, + 0x02F2, + 0x02D0, + 0x82D5, + 0x82DF, + 0x02DA, + 0x82CB, + 0x02CE, + 0x02C4, + 0x82C1, + 0x8243, + 0x0246, + 0x024C, + 0x8249, + 0x0258, + 0x825D, + 0x8257, + 0x0252, + 0x0270, + 0x8275, + 0x827F, + 0x027A, + 0x826B, + 0x026E, + 0x0264, + 0x8261, + 0x0220, + 0x8225, + 0x822F, + 0x022A, + 0x823B, + 0x023E, + 0x0234, + 0x8231, + 0x8213, + 0x0216, + 0x021C, + 0x8219, + 0x0208, + 0x820D, + 0x8207, + 0x0202, + ] for j in range(0, data_blk_size): i = ((crc_accum >> 8) ^ data_blk_ptr[j]) & 0xFF @@ -158,16 +380,20 @@ def addStuffing(self, packet): temp = [0] * TXPACKET_MAX_LEN # FF FF FD XX ID LEN_L LEN_H - temp[PKT_HEADER0: PKT_HEADER0 + PKT_LENGTH_H + 1] = packet[PKT_HEADER0: PKT_HEADER0 + PKT_LENGTH_H + 1] + temp[PKT_HEADER0 : PKT_HEADER0 + PKT_LENGTH_H + 1] = packet[ + PKT_HEADER0 : PKT_HEADER0 + PKT_LENGTH_H + 1 + ] index = PKT_INSTRUCTION for i in range(0, packet_length_in - 2): # except CRC temp[index] = packet[i + PKT_INSTRUCTION] index = index + 1 - if packet[i + PKT_INSTRUCTION] == 0xFD \ - and packet[i + PKT_INSTRUCTION - 1] == 0xFF \ - and packet[i + PKT_INSTRUCTION - 2] == 0xFF: + if ( + packet[i + PKT_INSTRUCTION] == 0xFD + and packet[i + PKT_INSTRUCTION - 1] == 0xFF + and packet[i + PKT_INSTRUCTION - 2] == 0xFF + ): # FF FF FD temp[index] = 0xFD index = index + 1 @@ -180,7 +406,7 @@ def addStuffing(self, packet): if packet_length_in != packet_length_out: packet = [0] * index - packet[0: index] = temp[0: index] + packet[0:index] = temp[0:index] packet[PKT_LENGTH_L] = DXL_LOBYTE(packet_length_out) packet[PKT_LENGTH_H] = DXL_HIBYTE(packet_length_out) @@ -193,8 +419,12 @@ def removeStuffing(self, packet): index = PKT_INSTRUCTION for i in range(0, (packet_length_in - 2)): # except CRC - if (packet[i + PKT_INSTRUCTION] == 0xFD) and (packet[i + PKT_INSTRUCTION + 1] == 0xFD) and ( - packet[i + PKT_INSTRUCTION - 1] == 0xFF) and (packet[i + PKT_INSTRUCTION - 2] == 0xFF): + if ( + (packet[i + PKT_INSTRUCTION] == 0xFD) + and (packet[i + PKT_INSTRUCTION + 1] == 0xFD) + and (packet[i + PKT_INSTRUCTION - 1] == 0xFF) + and (packet[i + PKT_INSTRUCTION - 2] == 0xFF) + ): # FF FF FD FD packet_length_out = packet_length_out - 1 else: @@ -218,7 +448,9 @@ def txPacket(self, port, txpacket): self.addStuffing(txpacket) # check max packet length - total_packet_length = DXL_MAKEWORD(txpacket[PKT_LENGTH_L], txpacket[PKT_LENGTH_H]) + 7 + total_packet_length = ( + DXL_MAKEWORD(txpacket[PKT_LENGTH_L], txpacket[PKT_LENGTH_H]) + 7 + ) # 7: HEADER0 HEADER1 HEADER2 RESERVED ID LENGTH_L LENGTH_H if total_packet_length > TXPACKET_MAX_LEN: @@ -259,21 +491,39 @@ def rxPacket(self, port): if rx_length >= wait_length: # find packet header for idx in range(0, (rx_length - 3)): - if (rxpacket[idx] == 0xFF) and (rxpacket[idx + 1] == 0xFF) and (rxpacket[idx + 2] == 0xFD) and ( - rxpacket[idx + 3] != 0xFD): + if ( + (rxpacket[idx] == 0xFF) + and (rxpacket[idx + 1] == 0xFF) + and (rxpacket[idx + 2] == 0xFD) + and (rxpacket[idx + 3] != 0xFD) + ): break if idx == 0: - if (rxpacket[PKT_RESERVED] != 0x00) or (rxpacket[PKT_ID] > 0xFC) or ( - DXL_MAKEWORD(rxpacket[PKT_LENGTH_L], rxpacket[PKT_LENGTH_H]) > RXPACKET_MAX_LEN) or ( - rxpacket[PKT_INSTRUCTION] != 0x55): + if ( + (rxpacket[PKT_RESERVED] != 0x00) + or (rxpacket[PKT_ID] > 0xFC) + or ( + DXL_MAKEWORD(rxpacket[PKT_LENGTH_L], rxpacket[PKT_LENGTH_H]) + > RXPACKET_MAX_LEN + ) + or (rxpacket[PKT_INSTRUCTION] != 0x55) + ): # remove the first byte in the packet del rxpacket[0] rx_length -= 1 continue - if wait_length != (DXL_MAKEWORD(rxpacket[PKT_LENGTH_L], rxpacket[PKT_LENGTH_H]) + PKT_LENGTH_H + 1): - wait_length = DXL_MAKEWORD(rxpacket[PKT_LENGTH_L], rxpacket[PKT_LENGTH_H]) + PKT_LENGTH_H + 1 + if wait_length != ( + DXL_MAKEWORD(rxpacket[PKT_LENGTH_L], rxpacket[PKT_LENGTH_H]) + + PKT_LENGTH_H + + 1 + ): + wait_length = ( + DXL_MAKEWORD(rxpacket[PKT_LENGTH_L], rxpacket[PKT_LENGTH_H]) + + PKT_LENGTH_H + + 1 + ) continue if rx_length < wait_length: # lgtm [py/redundant-comparison] @@ -286,7 +536,9 @@ def rxPacket(self, port): else: continue - crc = DXL_MAKEWORD(rxpacket[wait_length - 2], rxpacket[wait_length - 1]) + crc = DXL_MAKEWORD( + rxpacket[wait_length - 2], rxpacket[wait_length - 1] + ) if self.updateCRC(0, rxpacket, wait_length - 2) == crc: result = COMM_SUCCESS @@ -296,7 +548,7 @@ def rxPacket(self, port): else: # remove unnecessary packets - del rxpacket[0: idx] + del rxpacket[0:idx] rx_length -= idx else: @@ -325,7 +577,10 @@ def txRxPacket(self, port, txpacket): return rxpacket, result, error # (Instruction == BulkRead or SyncRead) == this function is not available. - if txpacket[PKT_INSTRUCTION] == INST_BULK_READ or txpacket[PKT_INSTRUCTION] == INST_SYNC_READ: + if ( + txpacket[PKT_INSTRUCTION] == INST_BULK_READ + or txpacket[PKT_INSTRUCTION] == INST_SYNC_READ + ): result = COMM_NOT_AVAILABLE # (ID == Broadcast ID) == no need to wait for status packet or not available. @@ -336,7 +591,10 @@ def txRxPacket(self, port, txpacket): # set packet timeout if txpacket[PKT_INSTRUCTION] == INST_READ: - port.setPacketTimeout(DXL_MAKEWORD(txpacket[PKT_PARAMETER0 + 2], txpacket[PKT_PARAMETER0 + 3]) + 11) + port.setPacketTimeout( + DXL_MAKEWORD(txpacket[PKT_PARAMETER0 + 2], txpacket[PKT_PARAMETER0 + 3]) + + 11 + ) else: port.setPacketTimeout(11) # HEADER0 HEADER1 HEADER2 RESERVED ID LENGTH_L LENGTH_H INST ERROR CRC16_L CRC16_H @@ -368,7 +626,9 @@ def ping(self, port, dxl_id): rxpacket, result, error = self.txRxPacket(port, txpacket) if result == COMM_SUCCESS: - model_number = DXL_MAKEWORD(rxpacket[PKT_PARAMETER0 + 1], rxpacket[PKT_PARAMETER0 + 2]) + model_number = DXL_MAKEWORD( + rxpacket[PKT_PARAMETER0 + 1], rxpacket[PKT_PARAMETER0 + 2] + ) return model_number, result, error @@ -383,7 +643,7 @@ def broadcastPing(self, port): txpacket = [0] * 10 rxpacket = [] - tx_time_per_byte = (1000.0 / port.getBaudRate()) *10.0; + tx_time_per_byte = (1000.0 / port.getBaudRate()) * 10.0 txpacket[PKT_ID] = BROADCAST_ID txpacket[PKT_LENGTH_L] = 3 @@ -396,8 +656,10 @@ def broadcastPing(self, port): return data_list, result # set rx timeout - #port.setPacketTimeout(wait_length * 1) - port.setPacketTimeoutMillis((wait_length * tx_time_per_byte) + (3.0 * MAX_ID) + 16.0); + # port.setPacketTimeout(wait_length * 1) + port.setPacketTimeoutMillis( + (wait_length * tx_time_per_byte) + (3.0 * MAX_ID) + 16.0 + ) while True: rxpacket += port.readPort(wait_length - rx_length) @@ -417,40 +679,49 @@ def broadcastPing(self, port): # find packet header for idx in range(0, rx_length - 2): - if rxpacket[idx] == 0xFF and rxpacket[idx + 1] == 0xFF and rxpacket[idx + 2] == 0xFD: + if ( + rxpacket[idx] == 0xFF + and rxpacket[idx + 1] == 0xFF + and rxpacket[idx + 2] == 0xFD + ): break if idx == 0: # found at the beginning of the packet # verify CRC16 - crc = DXL_MAKEWORD(rxpacket[STATUS_LENGTH - 2], rxpacket[STATUS_LENGTH - 1]) + crc = DXL_MAKEWORD( + rxpacket[STATUS_LENGTH - 2], rxpacket[STATUS_LENGTH - 1] + ) if self.updateCRC(0, rxpacket, STATUS_LENGTH - 2) == crc: result = COMM_SUCCESS data_list[rxpacket[PKT_ID]] = [ - DXL_MAKEWORD(rxpacket[PKT_PARAMETER0 + 1], rxpacket[PKT_PARAMETER0 + 2]), - rxpacket[PKT_PARAMETER0 + 3]] + DXL_MAKEWORD( + rxpacket[PKT_PARAMETER0 + 1], rxpacket[PKT_PARAMETER0 + 2] + ), + rxpacket[PKT_PARAMETER0 + 3], + ] - del rxpacket[0: STATUS_LENGTH] + del rxpacket[0:STATUS_LENGTH] rx_length = rx_length - STATUS_LENGTH if rx_length == 0: return data_list, result else: - result = COMM_RX_CORRUPT # lgtm [py/multiple-definition] + result = COMM_RX_CORRUPT # lgtm [py/multiple-definition] # remove header (0xFF 0xFF 0xFD) - del rxpacket[0: 3] + del rxpacket[0:3] rx_length = rx_length - 3 else: # remove unnecessary packets - del rxpacket[0: idx] + del rxpacket[0:idx] rx_length = rx_length - idx # FIXME: unreachable code - return data_list, result # lgtm [py/unreachable-statement] + return data_list, result # lgtm [py/unreachable-statement] def action(self, port, dxl_id): txpacket = [0] * 10 @@ -526,7 +797,7 @@ def readTx(self, port, dxl_id, address, length): return result def readRx(self, port, dxl_id, length): - result = COMM_TX_FAIL # lgtm [py/multiple-definition] + result = COMM_TX_FAIL # lgtm [py/multiple-definition] error = 0 rxpacket = None @@ -541,7 +812,7 @@ def readRx(self, port, dxl_id, length): if result == COMM_SUCCESS and rxpacket[PKT_ID] == dxl_id: error = rxpacket[PKT_ERROR] - data.extend(rxpacket[PKT_PARAMETER0 + 1: PKT_PARAMETER0 + 1 + length]) + data.extend(rxpacket[PKT_PARAMETER0 + 1 : PKT_PARAMETER0 + 1 + length]) return data, result, error @@ -567,7 +838,7 @@ def readTxRx(self, port, dxl_id, address, length): if result == COMM_SUCCESS: error = rxpacket[PKT_ERROR] - data.extend(rxpacket[PKT_PARAMETER0 + 1: PKT_PARAMETER0 + 1 + length]) + data.extend(rxpacket[PKT_PARAMETER0 + 1 : PKT_PARAMETER0 + 1 + length]) return data, result, error @@ -602,14 +873,24 @@ def read4ByteTx(self, port, dxl_id, address): def read4ByteRx(self, port, dxl_id): data, result, error = self.readRx(port, dxl_id, 4) - data_read = DXL_MAKEDWORD(DXL_MAKEWORD(data[0], data[1]), - DXL_MAKEWORD(data[2], data[3])) if (result == COMM_SUCCESS) else 0 + data_read = ( + DXL_MAKEDWORD( + DXL_MAKEWORD(data[0], data[1]), DXL_MAKEWORD(data[2], data[3]) + ) + if (result == COMM_SUCCESS) + else 0 + ) return data_read, result, error def read4ByteTxRx(self, port, dxl_id, address): data, result, error = self.readTxRx(port, dxl_id, address, 4) - data_read = DXL_MAKEDWORD(DXL_MAKEWORD(data[0], data[1]), - DXL_MAKEWORD(data[2], data[3])) if (result == COMM_SUCCESS) else 0 + data_read = ( + DXL_MAKEDWORD( + DXL_MAKEWORD(data[0], data[1]), DXL_MAKEWORD(data[2], data[3]) + ) + if (result == COMM_SUCCESS) + else 0 + ) return data_read, result, error def writeTxOnly(self, port, dxl_id, address, length, data): @@ -622,7 +903,7 @@ def writeTxOnly(self, port, dxl_id, address, length, data): txpacket[PKT_PARAMETER0 + 0] = DXL_LOBYTE(address) txpacket[PKT_PARAMETER0 + 1] = DXL_HIBYTE(address) - txpacket[PKT_PARAMETER0 + 2: PKT_PARAMETER0 + 2 + length] = data[0: length] + txpacket[PKT_PARAMETER0 + 2 : PKT_PARAMETER0 + 2 + length] = data[0:length] result = self.txPacket(port, txpacket) port.is_using = False @@ -639,7 +920,7 @@ def writeTxRx(self, port, dxl_id, address, length, data): txpacket[PKT_PARAMETER0 + 0] = DXL_LOBYTE(address) txpacket[PKT_PARAMETER0 + 1] = DXL_HIBYTE(address) - txpacket[PKT_PARAMETER0 + 2: PKT_PARAMETER0 + 2 + length] = data[0: length] + txpacket[PKT_PARAMETER0 + 2 : PKT_PARAMETER0 + 2 + length] = data[0:length] rxpacket, result, error = self.txRxPacket(port, txpacket) return result, error @@ -661,17 +942,21 @@ def write2ByteTxRx(self, port, dxl_id, address, data): return self.writeTxRx(port, dxl_id, address, 2, data_write) def write4ByteTxOnly(self, port, dxl_id, address, data): - data_write = [DXL_LOBYTE(DXL_LOWORD(data)), - DXL_HIBYTE(DXL_LOWORD(data)), - DXL_LOBYTE(DXL_HIWORD(data)), - DXL_HIBYTE(DXL_HIWORD(data))] + data_write = [ + DXL_LOBYTE(DXL_LOWORD(data)), + DXL_HIBYTE(DXL_LOWORD(data)), + DXL_LOBYTE(DXL_HIWORD(data)), + DXL_HIBYTE(DXL_HIWORD(data)), + ] return self.writeTxOnly(port, dxl_id, address, 4, data_write) def write4ByteTxRx(self, port, dxl_id, address, data): - data_write = [DXL_LOBYTE(DXL_LOWORD(data)), - DXL_HIBYTE(DXL_LOWORD(data)), - DXL_LOBYTE(DXL_HIWORD(data)), - DXL_HIBYTE(DXL_HIWORD(data))] + data_write = [ + DXL_LOBYTE(DXL_LOWORD(data)), + DXL_HIBYTE(DXL_LOWORD(data)), + DXL_LOBYTE(DXL_HIWORD(data)), + DXL_HIBYTE(DXL_HIWORD(data)), + ] return self.writeTxRx(port, dxl_id, address, 4, data_write) def regWriteTxOnly(self, port, dxl_id, address, length, data): @@ -684,7 +969,7 @@ def regWriteTxOnly(self, port, dxl_id, address, length, data): txpacket[PKT_PARAMETER0 + 0] = DXL_LOBYTE(address) txpacket[PKT_PARAMETER0 + 1] = DXL_HIBYTE(address) - txpacket[PKT_PARAMETER0 + 2: PKT_PARAMETER0 + 2 + length] = data[0: length] + txpacket[PKT_PARAMETER0 + 2 : PKT_PARAMETER0 + 2 + length] = data[0:length] result = self.txPacket(port, txpacket) port.is_using = False @@ -701,7 +986,7 @@ def regWriteTxRx(self, port, dxl_id, address, length, data): txpacket[PKT_PARAMETER0 + 0] = DXL_LOBYTE(address) txpacket[PKT_PARAMETER0 + 1] = DXL_HIBYTE(address) - txpacket[PKT_PARAMETER0 + 2: PKT_PARAMETER0 + 2 + length] = data[0: length] + txpacket[PKT_PARAMETER0 + 2 : PKT_PARAMETER0 + 2 + length] = data[0:length] _, result, error = self.txRxPacket(port, txpacket) @@ -713,16 +998,20 @@ def syncReadTx(self, port, start_address, data_length, param, param_length): txpacket[PKT_ID] = BROADCAST_ID txpacket[PKT_LENGTH_L] = DXL_LOBYTE( - param_length + 7) # 7: INST START_ADDR_L START_ADDR_H DATA_LEN_L DATA_LEN_H CRC16_L CRC16_H + param_length + 7 + ) # 7: INST START_ADDR_L START_ADDR_H DATA_LEN_L DATA_LEN_H CRC16_L CRC16_H txpacket[PKT_LENGTH_H] = DXL_HIBYTE( - param_length + 7) # 7: INST START_ADDR_L START_ADDR_H DATA_LEN_L DATA_LEN_H CRC16_L CRC16_H + param_length + 7 + ) # 7: INST START_ADDR_L START_ADDR_H DATA_LEN_L DATA_LEN_H CRC16_L CRC16_H txpacket[PKT_INSTRUCTION] = INST_SYNC_READ txpacket[PKT_PARAMETER0 + 0] = DXL_LOBYTE(start_address) txpacket[PKT_PARAMETER0 + 1] = DXL_HIBYTE(start_address) txpacket[PKT_PARAMETER0 + 2] = DXL_LOBYTE(data_length) txpacket[PKT_PARAMETER0 + 3] = DXL_HIBYTE(data_length) - txpacket[PKT_PARAMETER0 + 4: PKT_PARAMETER0 + 4 + param_length] = param[0: param_length] + txpacket[PKT_PARAMETER0 + 4 : PKT_PARAMETER0 + 4 + param_length] = param[ + 0:param_length + ] result = self.txPacket(port, txpacket) if result == COMM_SUCCESS: @@ -736,16 +1025,20 @@ def syncWriteTxOnly(self, port, start_address, data_length, param, param_length) txpacket[PKT_ID] = BROADCAST_ID txpacket[PKT_LENGTH_L] = DXL_LOBYTE( - param_length + 7) # 7: INST START_ADDR_L START_ADDR_H DATA_LEN_L DATA_LEN_H CRC16_L CRC16_H + param_length + 7 + ) # 7: INST START_ADDR_L START_ADDR_H DATA_LEN_L DATA_LEN_H CRC16_L CRC16_H txpacket[PKT_LENGTH_H] = DXL_HIBYTE( - param_length + 7) # 7: INST START_ADDR_L START_ADDR_H DATA_LEN_L DATA_LEN_H CRC16_L CRC16_H + param_length + 7 + ) # 7: INST START_ADDR_L START_ADDR_H DATA_LEN_L DATA_LEN_H CRC16_L CRC16_H txpacket[PKT_INSTRUCTION] = INST_SYNC_WRITE txpacket[PKT_PARAMETER0 + 0] = DXL_LOBYTE(start_address) txpacket[PKT_PARAMETER0 + 1] = DXL_HIBYTE(start_address) txpacket[PKT_PARAMETER0 + 2] = DXL_LOBYTE(data_length) txpacket[PKT_PARAMETER0 + 3] = DXL_HIBYTE(data_length) - txpacket[PKT_PARAMETER0 + 4: PKT_PARAMETER0 + 4 + param_length] = param[0: param_length] + txpacket[PKT_PARAMETER0 + 4 : PKT_PARAMETER0 + 4 + param_length] = param[ + 0:param_length + ] _, result, _ = self.txRxPacket(port, txpacket) @@ -760,7 +1053,7 @@ def bulkReadTx(self, port, param, param_length): txpacket[PKT_LENGTH_H] = DXL_HIBYTE(param_length + 3) # 3: INST CRC16_L CRC16_H txpacket[PKT_INSTRUCTION] = INST_BULK_READ - txpacket[PKT_PARAMETER0: PKT_PARAMETER0 + param_length] = param[0: param_length] + txpacket[PKT_PARAMETER0 : PKT_PARAMETER0 + param_length] = param[0:param_length] result = self.txPacket(port, txpacket) if result == COMM_SUCCESS: @@ -782,7 +1075,7 @@ def bulkWriteTxOnly(self, port, param, param_length): txpacket[PKT_LENGTH_H] = DXL_HIBYTE(param_length + 3) # 3: INST CRC16_L CRC16_H txpacket[PKT_INSTRUCTION] = INST_BULK_WRITE - txpacket[PKT_PARAMETER0: PKT_PARAMETER0 + param_length] = param[0: param_length] + txpacket[PKT_PARAMETER0 : PKT_PARAMETER0 + param_length] = param[0:param_length] _, result, _ = self.txRxPacket(port, txpacket) diff --git a/roboticstoolbox/backends/Dynamixel/dyndata.py b/roboticstoolbox/backends/Dynamixel/dyndata.py index 8ece2363d..53a92bf34 100644 --- a/roboticstoolbox/backends/Dynamixel/dyndata.py +++ b/roboticstoolbox/backends/Dynamixel/dyndata.py @@ -6,37 +6,48 @@ url = "https://emanual.robotis.com/docs/en/software/dynamixel/dynamixel_workbench/" + def table1_element(tables, name): try: - value = tables[0].find(string=re.compile(name)).parent.find_next_siblings("td")[0] + value = ( + tables[0].find(string=re.compile(name)).parent.find_next_siblings("td")[0] + ) print(name, value.text) return value.text except Exception: - print(name, ' not found') + print(name, " not found") return None + def table2_element(tables, name, col=2): try: - value = tables[1].find(string=re.compile(name)).parent.parent.find_next_siblings("td")[col] + value = ( + tables[1] + .find(string=re.compile(name)) + .parent.parent.find_next_siblings("td")[col] + ) print(name, value.text) return value.text except Exception: - print(name, ' not found') + print(name, " not found") return None + def getint(s): - print('getint: ', s) - r = re.compile('[0-9,]+') + print("getint: ", s) + r = re.compile("[0-9,]+") m = r.search(s) if m is None: return None else: - return int(m.group(0).replace(',', '')) + return int(m.group(0).replace(",", "")) + def getfloat(s): - r = re.compile('[0-9,\.]+') + r = re.compile("[0-9,\.]+") m = r.search(s) - return float(m.group(0).replace(',', '')) + return float(m.group(0).replace(",", "")) + with urllib.request.urlopen(url) as response: html = response.read() @@ -46,19 +57,19 @@ def getfloat(s): h = soup.find_all("h1", id="supported-dynamixel") table = h[0].find_next_sibling("table") models = [] - for tr in table.find_all('tr'): - for a in tr.find_all('a'): - print( a.text, "https://emanual.robotis.com" + a['href']) - models.append((a.text, "https://emanual.robotis.com" + a['href'])) + for tr in table.find_all("tr"): + for a in tr.find_all("a"): + print(a.text, "https://emanual.robotis.com" + a["href"]) + models.append((a.text, "https://emanual.robotis.com" + a["href"])) -dyndata = namedtuple('dynamixel_data', 'model res posmin posmax') +dyndata = namedtuple("dynamixel_data", "model res posmin posmax") dyndict = {} for model, url in models: - print('MODEL', model) + print("MODEL", model) with urllib.request.urlopen(url) as response: motor = {} - motor['model'] = model - motor['url'] = url + motor["model"] = model + motor["url"] = url html = response.read() @@ -67,44 +78,44 @@ def getfloat(s): # first table, find resolution res = table1_element(tables, "Resolution.*") - r = res.split(' ') + r = res.split(" ") r[0] = getfloat(r[0]) - motor['resolution'] = r + motor["resolution"] = r rd = table1_element(tables, "Running Degree.*") if rd is not None: # 0 [deg] ~ 300 [deg] - rd = rd.split('~') - motor['angular_range'] = [getint(x) for x in rd] + rd = rd.split("~") + motor["angular_range"] = [getint(x) for x in rd] # second table, find min/max position, model col = 2 model = table2_element(tables, "Model.*") - if model == '-': + if model == "-": # some series (eg. X) have one less column in this table col = 1 model = table2_element(tables, "Model.*", col) model = getint(model) - motor['modelnum'] = model + motor["modelnum"] = model cw = table2_element(tables, "CW Angle Limit.*", col) if cw is not None: cw = getint(cw) ccw = table2_element(tables, "CCW Angle Limit.*", col) ccw = getint(ccw) - motor['encoder_range'] = (cw, ccw) + motor["encoder_range"] = (cw, ccw) max = table2_element(tables[1], "Max.* Pos.*", col) if max is not None: - max = int(max.text.replace(',', '')) + max = int(max.text.replace(",", "")) min = table2_element(tables[1], "Min.* Pos.*", col) - min = int(min.text.replace(',', '')) - motor['encoder_range'] = (min, max) + min = int(min.text.replace(",", "")) + motor["encoder_range"] = (min, max) dyndict[model] = motor print(dyndict) -with open('dynamixel.json', 'w') as outfile: - json.dump(dyndict, outfile, indent=4) \ No newline at end of file +with open("dynamixel.json", "w") as outfile: + json.dump(dyndict, outfile, indent=4) diff --git a/roboticstoolbox/backends/PyPlot/EllipsePlot.py b/roboticstoolbox/backends/PyPlot/EllipsePlot.py index 1fff39ecf..bd8a5ad61 100644 --- a/roboticstoolbox/backends/PyPlot/EllipsePlot.py +++ b/roboticstoolbox/backends/PyPlot/EllipsePlot.py @@ -190,7 +190,7 @@ def make_ellipsoid2(self): # force ellipsoid is E = A # # rather than compute the inverse we can have base.ellipse() do it for us - # using the inverted argument. For the velocity ellipse we already have the + # using the inverted argument. For the velocity ellipse we already have the # inverse of E, it is A, so we set inverted=True. if isinstance(self.centre, str) and self.centre == "ee": diff --git a/roboticstoolbox/backends/PyPlot/PyPlot.py b/roboticstoolbox/backends/PyPlot/PyPlot.py index f5149a78f..e4a58d905 100644 --- a/roboticstoolbox/backends/PyPlot/PyPlot.py +++ b/roboticstoolbox/backends/PyPlot/PyPlot.py @@ -2,6 +2,7 @@ """ @author Jesse Haviland """ + import time import roboticstoolbox as rp import numpy as np @@ -65,7 +66,6 @@ class PyPlot(Connector): """ def __init__(self): - super(PyPlot, self).__init__() self.robots = [] self.ellipses = [] @@ -469,7 +469,6 @@ def _set_axes_equal(self): self.ax.set_zlim3d([z_middle - plot_radius, z_middle + plot_radius]) def _add_teach_panel(self, robot, q): - if _isnotebook(): raise RuntimeError("cannot use teach panel under Jupyter") diff --git a/roboticstoolbox/backends/PyPlot/PyPlot2.py b/roboticstoolbox/backends/PyPlot/PyPlot2.py index d37016b7e..711adab51 100644 --- a/roboticstoolbox/backends/PyPlot/PyPlot2.py +++ b/roboticstoolbox/backends/PyPlot/PyPlot2.py @@ -34,7 +34,6 @@ class PyPlot2(Connector): def __init__(self): - super(PyPlot2, self).__init__() self.robots = [] self.ellipses = [] @@ -217,7 +216,6 @@ def hold(self): # pragma: no cover # def _step_robots(self, dt): - for rpl in self.robots: robot = rpl.robot @@ -225,7 +223,6 @@ def _step_robots(self, dt): pass # pragma: no cover elif robot.control_type == "v": - for i in range(robot.n): robot.q[i] += robot.qd[i] * (dt / 1000) @@ -242,12 +239,10 @@ def _update_robots(self): pass def _draw_robots(self): - for i in range(len(self.robots)): self.robots[i].draw() def _draw_ellipses(self): - for i in range(len(self.ellipses)): self.ellipses[i].draw2() diff --git a/roboticstoolbox/backends/PyPlot/RobotPlot2.py b/roboticstoolbox/backends/PyPlot/RobotPlot2.py index c9f1c3913..50b0c3078 100644 --- a/roboticstoolbox/backends/PyPlot/RobotPlot2.py +++ b/roboticstoolbox/backends/PyPlot/RobotPlot2.py @@ -7,23 +7,29 @@ from roboticstoolbox.backends.PyPlot.RobotPlot import RobotPlot import numpy as np from spatialmath import SE2 -class RobotPlot2(RobotPlot): - def __init__( - self, robot, env, readonly, display=True, - eeframe=True, name=True, options=None): +class RobotPlot2(RobotPlot): + def __init__( + self, robot, env, readonly, display=True, eeframe=True, name=True, options=None + ): super().__init__( - robot, env, readonly, display=display, - jointaxes=False, shadow=False, eeframe=eeframe, name=name + robot, + env, + readonly, + display=display, + jointaxes=False, + shadow=False, + eeframe=eeframe, + name=name, ) defaults = { - 'robot': {'color': '#E16F6D', 'linewidth': 5}, - 'jointlabels': {}, - 'eex': {'color': '#F84752', 'linewidth': 2}, # '#EE9494' - 'eey': {'color': '#BADA55', 'linewidth': 2}, # '#93E7B0' - 'eelength': 0.06, + "robot": {"color": "#E16F6D", "linewidth": 5}, + "jointlabels": {}, + "eex": {"color": "#F84752", "linewidth": 2}, # '#EE9494' + "eey": {"color": "#BADA55", "linewidth": 2}, # '#93E7B0' + "eelength": 0.06, } if options is not None: @@ -43,7 +49,7 @@ def draw(self): # compute all link frames T = self.robot.fkine_all(self.robot.q) - + # draw all the line segments for the noodle plot for i, segment in enumerate(self.segments): linkframes = [] @@ -54,8 +60,8 @@ def draw(self): linkframes.append(T[link.number]) points = np.array([linkframe.t for linkframe in linkframes]) - self.links[i].set_xdata(points[:,0]) - self.links[i].set_ydata(points[:,1]) + self.links[i].set_xdata(points[:, 0]) + self.links[i].set_ydata(points[:, 1]) ## Draw the end-effectors @@ -67,11 +73,10 @@ def draw(self): self.eeframes = [] if self.eeframe: - len = self.options['eelength'] + len = self.options["eelength"] Tjx = SE2(len, 0) Tjy = SE2(0, len) - # add new ee coordinate frame for link in self.robot.ee_links: Te = T[link.number] @@ -80,13 +85,12 @@ def draw(self): Tex = Te * Tjx Tey = Te * Tjy - xaxis = self._plot_quiver(Te.t, Tex.t, self.options['eex']) - yaxis = self._plot_quiver(Te.t, Tey.t, self.options['eey']) + xaxis = self._plot_quiver(Te.t, Tex.t, self.options["eex"]) + yaxis = self._plot_quiver(Te.t, Tey.t, self.options["eey"]) self.eeframes.extend([xaxis, yaxis]) def init(self): - self.drawn = True limits = np.r_[-1, 1, -1, 1] * self.robot.reach * 1.5 @@ -101,24 +105,17 @@ def init(self): # Plot robot name if self.showname: - self.name = self.ax.text( - Tb.t[0] + 0.05, Tb.t[1], self.robot.name) + self.name = self.ax.text(Tb.t[0] + 0.05, Tb.t[1], self.robot.name) # Initialize the robot links self.links = [] for i in range(len(self.segments)): - line, = self.ax.plot( - 0, 0, **self.options['robot']) + (line,) = self.ax.plot(0, 0, **self.options["robot"]) self.links.append(line) - + self.eeframes = [] def _plot_quiver(self, p0, p1, options): # draw arrow from p0 (tail) to p1 (head) - qv = self.ax.quiver( - p0[0], p0[1], - p1[0] - p0[0], - p1[1] - p0[1], - **options - ) - return qv \ No newline at end of file + qv = self.ax.quiver(p0[0], p0[1], p1[0] - p0[0], p1[1] - p0[1], **options) + return qv diff --git a/roboticstoolbox/backends/PyPlot/__init__.py b/roboticstoolbox/backends/PyPlot/__init__.py index e3900576e..e849bec70 100644 --- a/roboticstoolbox/backends/PyPlot/__init__.py +++ b/roboticstoolbox/backends/PyPlot/__init__.py @@ -1,7 +1,4 @@ from roboticstoolbox.backends.PyPlot.PyPlot import PyPlot from roboticstoolbox.backends.PyPlot.PyPlot2 import PyPlot2 -__all__ = [ - 'PyPlot', - 'PyPlot2' -] +__all__ = ["PyPlot", "PyPlot2"] diff --git a/roboticstoolbox/backends/ROS/ROS.py b/roboticstoolbox/backends/ROS/ROS.py index b43866e85..ff270a70b 100644 --- a/roboticstoolbox/backends/ROS/ROS.py +++ b/roboticstoolbox/backends/ROS/ROS.py @@ -6,7 +6,6 @@ from roboticstoolbox.backends.Connector import Connector import roboticstoolbox as rtb import numpy as np -import spatialmath as sm import time import subprocess @@ -18,21 +17,20 @@ rospy = None -def _import_ros(): # pragma nocover +def _import_ros(): # pragma nocover import importlib + global rospy try: - ros = importlib.import_module('rospy') + ros = importlib.import_module("rospy") except ImportError: - print( - '\nYou must have ROS installed') + print("\nYou must have ROS installed") raise class ROS(Connector): # pragma nocover - """ + """ """ - """ def __init__(self): super(ROS, self).__init__() @@ -43,15 +41,14 @@ def __init__(self): # def launch(self, roscore=False, ros_master_uri=None, ros_ip=None): - """ - """ + """ """ super().launch() # Launch roscore in seperate process if roscore: - print('Launching ROS core\n') - self.roscore = subprocess.Popen('roscore') + print("Launching ROS core\n") + self.roscore = subprocess.Popen("roscore") # Give it some time to launch time.sleep(1) @@ -65,32 +62,27 @@ def launch(self, roscore=False, ros_master_uri=None, ros_ip=None): v = VelPub(rtb.models.Panda()) def step(self, dt=0.05): - """ - """ + """ """ super().step def reset(self): - """ - """ + """ """ super().reset def restart(self): - """ - """ + """ """ super().restart def close(self): - """ - """ + """ """ super().close() def hold(self): - """ - """ + """ """ super().hold() @@ -98,10 +90,8 @@ def hold(self): # Methods to interface with the robots created in other environemnts # - def add( - self, ob): - """ - """ + def add(self, ob): + """ """ super().add() @@ -120,9 +110,8 @@ def remove(self): class VelPub: - def __init__(self, robot): - rospy.init_node('RTB_VelocityPublisher') + rospy.init_node("RTB_VelocityPublisher") self.robot = robot self.v = np.zeros(robot.n) diff --git a/roboticstoolbox/backends/ROS/__init__.py b/roboticstoolbox/backends/ROS/__init__.py index 50dccb83e..260e3eaac 100644 --- a/roboticstoolbox/backends/ROS/__init__.py +++ b/roboticstoolbox/backends/ROS/__init__.py @@ -1,5 +1,3 @@ from roboticstoolbox.backends.ROS.ROS import ROS -__all__ = [ - 'ROS' -] +__all__ = ["ROS"] diff --git a/roboticstoolbox/backends/swift/__init__.py b/roboticstoolbox/backends/swift/__init__.py index 314e70b27..ba74ff763 100644 --- a/roboticstoolbox/backends/swift/__init__.py +++ b/roboticstoolbox/backends/swift/__init__.py @@ -1,19 +1,26 @@ from swift.SwiftRoute import SwiftServer, SwiftSocket, start_servers -from swift.SwiftElement import SwiftElement, Slider, Select, \ - Checkbox, Radio, Button, Label +from swift.SwiftElement import ( + SwiftElement, + Slider, + Select, + Checkbox, + Radio, + Button, + Label, +) from swift.Swift import Swift __all__ = [ - 'Swift', - 'Slider', - 'SwiftElement', - 'Label', - 'Select', - 'Button', - 'Checkbox', - 'Radio', - 'SwiftServer', - 'SwiftSocket', - 'start_servers' + "Swift", + "Slider", + "SwiftElement", + "Label", + "Select", + "Button", + "Checkbox", + "Radio", + "SwiftServer", + "SwiftSocket", + "start_servers", ] diff --git a/roboticstoolbox/bin/rtbtool.py b/roboticstoolbox/bin/rtbtool.py index 27c26f07a..5f6325856 100755 --- a/roboticstoolbox/bin/rtbtool.py +++ b/roboticstoolbox/bin/rtbtool.py @@ -15,7 +15,6 @@ # import stuff from pygments.token import Token from IPython.terminal.prompts import Prompts -from IPython.terminal.prompts import ClassicPrompts from traitlets.config import Config import IPython import argparse @@ -24,7 +23,6 @@ from importlib.metadata import version - try: from colored import fg, bg, attr @@ -35,24 +33,21 @@ _colored = False # imports for use by IPython and user -import math -from math import pi # lgtm [py/unused-import] import numpy as np -from scipy import linalg, optimize import matplotlib.pyplot as plt # lgtm [py/unused-import] from spatialmath import * # lgtm [py/polluting-import] from spatialmath.base import * -import spatialmath.base as smb -from spatialmath.base import sym from spatialgeometry import * # lgtm [py/polluting-import] from roboticstoolbox import * # lgtm [py/unused-import] + # load some robot models puma = models.DH.Puma560() panda = models.DH.Panda() + def parse_arguments(): parser = argparse.ArgumentParser("Robotics Toolbox shell") parser.add_argument("script", default=None, nargs="?", help="specify script to run") @@ -65,10 +60,8 @@ def parse_arguments(): default="neutral", help="specify terminal color scheme (neutral, lightbg, nocolor, linux), linux is for dark mode", ) - parser.add_argument("--confirmexit", "-x", default=False, - help="confirm exit") - parser.add_argument("--prompt", "-p", default="(rtb) >>> ", - help="input prompt") + parser.add_argument("--confirmexit", "-x", default=False, help="confirm exit") + parser.add_argument("--prompt", "-p", default="(rtb) >>> ", help="input prompt") parser.add_argument( "--resultprefix", "-r", @@ -91,8 +84,10 @@ def parse_arguments(): help="do not display the result of assignments", ) parser.add_argument( - "--book", default=False, action="store_true", - help="use defaults as per RVC book" + "--book", + default=False, + action="store_true", + help="use defaults as per RVC book", ) parser.add_argument( "--ansi", @@ -129,17 +124,18 @@ def parse_arguments(): return args + def make_banner(): # banner template # https://patorjk.com/software/taag/#p=display&f=Cybermedium&t=Robotics%20Toolbox%0A - banner = f"""\ + banner = """\ ____ ____ ___ ____ ___ _ ____ ____ ___ ____ ____ _ ___ ____ _ _ |__/ | | |__] | | | | | [__ | | | | | | |__] | | \/ | \ |__| |__] |__| | | |___ ___] | |__| |__| |___ |__] |__| _/\_ for Python""" - + versions = [] versions.append(f"RTB=={version('roboticstoolbox-python')}") versions.append(f"SMTB=={version('spatialmath-python')}") @@ -174,14 +170,14 @@ def make_banner(): print(fg("yellow") + banner + attr(0)) + def startup(): plt.ion() -def main(): +def main(): args = parse_arguments() - # setup defaults np.set_printoptions( linewidth=120, @@ -210,7 +206,6 @@ def main(): # build the banner, import * packages and their versions - if args.banner: banner = make_banner() print(banner) @@ -269,7 +264,6 @@ def out_prompt_tokens(self, cli=None): else: code.append("plt.ion()") - c.InteractiveShellApp.exec_lines = code IPython.start_ipython(config=c, user_ns=globals()) diff --git a/roboticstoolbox/blocks/arm.py b/roboticstoolbox/blocks/arm.py index e89238eb7..f61ed6a8d 100644 --- a/roboticstoolbox/blocks/arm.py +++ b/roboticstoolbox/blocks/arm.py @@ -2,7 +2,6 @@ from math import sin, cos, pi # import matplotlib.pyplot as plt -import time from spatialmath import SE3 import spatialmath.base as smb @@ -1482,7 +1481,7 @@ def __init__( :type velcomp: bool :param representation: task-space representation, defaults to "rpy/xyz" :type representation: str - + :param blockargs: |BlockOptions| :type blockargs: dict """ diff --git a/roboticstoolbox/blocks/mobile.py b/roboticstoolbox/blocks/mobile.py index 557027750..f0c0a2c5d 100644 --- a/roboticstoolbox/blocks/mobile.py +++ b/roboticstoolbox/blocks/mobile.py @@ -1,16 +1,14 @@ -from typing import Type import numpy as np -from math import sin, cos, atan2, tan, sqrt, pi +from math import pi import matplotlib.pyplot as plt -from matplotlib.patches import Polygon -import time from bdsim.components import TransferBlock from bdsim.graphics import GraphicsBlock from roboticstoolbox import mobile + # ------------------------------------------------------------------------ # class Bicycle(TransferBlock): r""" @@ -154,6 +152,7 @@ class Unicycle(TransferBlock): :seealso: :class:`~roboticstoolbox.mobile.Vehicle.Unicycle` :class:`Bicycle` :class:`DiffSteer` """ + nin = 2 nout = 1 inlabels = ("v", "ω") diff --git a/roboticstoolbox/blocks/spatial.py b/roboticstoolbox/blocks/spatial.py index fcb4113cc..b9fab0e4f 100644 --- a/roboticstoolbox/blocks/spatial.py +++ b/roboticstoolbox/blocks/spatial.py @@ -1,15 +1,8 @@ -import numpy as np -from math import sin, cos, pi - # import matplotlib.pyplot as plt -import time from spatialmath import SE3 import spatialmath.base as smb -from bdsim.components import TransferBlock, FunctionBlock, SourceBlock -from bdsim.graphics import GraphicsBlock - -from roboticstoolbox import quintic_func, trapezoidal_func +from bdsim.components import FunctionBlock class Tr2Delta(FunctionBlock): @@ -237,7 +230,6 @@ def output(self, t, inports, x): if __name__ == "__main__": - from pathlib import Path exec( diff --git a/roboticstoolbox/blocks/uav.py b/roboticstoolbox/blocks/uav.py index 18381b0fa..b3146638e 100644 --- a/roboticstoolbox/blocks/uav.py +++ b/roboticstoolbox/blocks/uav.py @@ -1,8 +1,7 @@ import numpy as np -from math import sin, cos, atan2, tan, sqrt, pi +from math import sin, cos, atan2, sqrt, pi import matplotlib.pyplot as plt -import time from bdsim.components import TransferBlock, FunctionBlock from bdsim.graphics import GraphicsBlock @@ -38,9 +37,9 @@ class MultiRotor(TransferBlock): vehicle state is a dict containing the following items: - ``x`` pose in the world frame as :math:`[x, y, z, \theta_Y, \theta_P, \theta_R]` - - ``trans`` position and velocity in the world frame as + - ``trans`` position and velocity in the world frame as :math:`[x, y, z, \dot{x}, \dot{y}, \dot{z}]` - - ``rot`` orientation and angular rate in the world frame as + - ``rot`` orientation and angular rate in the world frame as :math:`[\theta_Y, \theta_P, \theta_R, \dot{\theta_Y}, \dot{\theta_P}, \dot{\theta_R}]` - ``vb`` translational velocity in the body frame as :math:`[\dot{x}, \dot{y}, \dot{z}]` @@ -48,7 +47,7 @@ class MultiRotor(TransferBlock): :math:`[\dot{\theta_Y}, \dot{\theta_P}, \dot{\theta_R}]` - ``a1s`` longitudinal flapping angles (radians) - ``b1s`` lateral flapping angles (radians) - - ``X`` full state vector as + - ``X`` full state vector as :math:`[x, y, z, \theta_Y, \theta_P, \theta_R, \dot{x}, \dot{y}, \dot{z}, \dot{\theta_Y}, \dot{\theta_P}, \dot{\theta_R}]` The dynamic model is a dict with the following key/value pairs. @@ -219,7 +218,6 @@ def __init__(self, model, groundcheck=True, speedcheck=True, x0=None, **blockarg self.b1s = np.zeros((self.nrotors,)) def output(self, t, inports, x): - model = self.model # compute output vector as a function of state vector @@ -270,8 +268,10 @@ def output(self, t, inports, x): out["x"] = x[0:6] out["trans"] = np.r_[x[:3], vd] out["rot"] = np.r_[x[3:6], rpyd] - out["vb"] = np.linalg.inv(R) @ x[6:9] # translational velocity mapped to body frame - out["w"] = iW @ x[9:12] # RPY rates mapped to body frame + out["vb"] = ( + np.linalg.inv(R) @ x[6:9] + ) # translational velocity mapped to body frame + out["w"] = iW @ x[9:12] # RPY rates mapped to body frame out["a1s"] = self.a1s out["b1s"] = self.b1s @@ -284,7 +284,6 @@ def output(self, t, inports, x): return [out] def deriv(self, t, inports, x): - model = self.model # Body-fixed frame references @@ -368,7 +367,6 @@ def deriv(self, t, inports, x): b1s = self.b1s for i in range(0, self.nrotors): # for each rotor - # Relative motion Vr = np.cross(o, self.D[:, i]) + v mu = sqrt(np.sum(Vr[0:2] ** 2)) / ( @@ -836,9 +834,7 @@ def plot3(h, x, y, z): for i in range(0, self.nrotors): hub[:, i] = F @ (z + R @ self.D[:, i]) # points in the inertial frame - q = ( - self.flapscale - ) # Flapping angle scaling for output display - makes it easier to see what flapping is occurring + q = self.flapscale # Flapping angle scaling for output display - makes it easier to see what flapping is occurring # Rotor -> Plot frame Rr = np.array( [ @@ -889,7 +885,6 @@ def plot3(h, x, y, z): if __name__ == "__main__": - from quad_model import quadrotor # m = MultiRotorMixer(model=quadrotor) diff --git a/roboticstoolbox/examples/RRMC.py b/roboticstoolbox/examples/RRMC.py index a86bed24d..cf1b4b5a1 100644 --- a/roboticstoolbox/examples/RRMC.py +++ b/roboticstoolbox/examples/RRMC.py @@ -10,7 +10,7 @@ import time env = PyPlot() -env.launch('Panda Resolved-Rate Motion Control Example') +env.launch("Panda Resolved-Rate Motion Control Example") panda = rtb.models.DH.Panda() panda.q = panda.qr @@ -23,7 +23,6 @@ dt = 0.05 while not arrived: - start = time.time() v, arrived = rtb.p_servo(panda.fkine(panda.q), Tep, 1) panda.qd = np.linalg.pinv(panda.jacobe(panda.q)) @ v diff --git a/roboticstoolbox/examples/RRMC_swift.py b/roboticstoolbox/examples/RRMC_swift.py index b6be04856..77f5a8dca 100644 --- a/roboticstoolbox/examples/RRMC_swift.py +++ b/roboticstoolbox/examples/RRMC_swift.py @@ -22,7 +22,6 @@ dt = 0.05 while not arrived: - v, arrived = rp.p_servo(panda.fkine(panda.q), Tep, 1) panda.qd = np.linalg.pinv(panda.jacobe(panda.q)) @ v env.step(dt) diff --git a/roboticstoolbox/examples/baur.py b/roboticstoolbox/examples/baur.py index 6bd0cc4ca..b81773953 100644 --- a/roboticstoolbox/examples/baur.py +++ b/roboticstoolbox/examples/baur.py @@ -38,7 +38,6 @@ arrived = False while not arrived: - # The pose of the Panda's end-effector Te = panda.fkine(panda.q) @@ -69,19 +68,21 @@ for i in range(n): if panda.q[i] - panda.qlim[0, i] <= pi: - b[i, 0] = -1 * \ - np.power(((panda.q[i] - panda.qlim[0, i]) - pi), 2) \ + b[i, 0] = ( + -1 + * np.power(((panda.q[i] - panda.qlim[0, i]) - pi), 2) / np.power((ps - pi), 2) + ) if panda.qlim[1, i] - panda.q[i] <= pi: - b[i, 0] = 1 * \ - np.power(((panda.qlim[1, i] - panda.q[i]) - pi), 2) \ + b[i, 0] = ( + 1 + * np.power(((panda.qlim[1, i] - panda.q[i]) - pi), 2) / np.power((ps - pi), 2) + ) # Project the gradient of manipulability into the null-space of the # differential kinematics - null = ( - np.eye(n) - np.linalg.pinv(Je) @ Je - ) @ (Jm - b) + null = (np.eye(n) - np.linalg.pinv(Je) @ Je) @ (Jm - b) # Solve for the joint velocities dq qd = np.linalg.pinv(Je) @ v + 1 / Y * null.flatten() diff --git a/roboticstoolbox/examples/branched_robot.py b/roboticstoolbox/examples/branched_robot.py index e6d22bbae..d5b43524d 100644 --- a/roboticstoolbox/examples/branched_robot.py +++ b/roboticstoolbox/examples/branched_robot.py @@ -68,7 +68,6 @@ gain = np.array([1, 1, 1, 1.6, 1.6, 1.6]) while not arrivedl or not arrivedr: - vl, arrivedl = rtb.p_servo(la.fkine(r.q), lTep, gain=gain, threshold=0.001) vr, arrivedr = rtb.p_servo(ra.fkine(r.q), rTep, gain=gain, threshold=0.001) diff --git a/roboticstoolbox/examples/eigdemo.py b/roboticstoolbox/examples/eigdemo.py index 71b863a59..0c16cf1a7 100644 --- a/roboticstoolbox/examples/eigdemo.py +++ b/roboticstoolbox/examples/eigdemo.py @@ -10,6 +10,7 @@ negative if the hands are anti-parallel. """ + import sys import numpy as np import matplotlib.pyplot as plt @@ -18,8 +19,7 @@ def eigdemo(A): - - print('matrix A =') + print("matrix A =") print(A) e, x = np.linalg.eig(A) @@ -31,27 +31,25 @@ def eigdemo(A): fig, ax = plt.subplots() plt.axis([-s, s, -s, s]) plt.grid(True) - plt.title('Eigenvector demonstration') - plt.xlabel('x') - plt.ylabel('y') - plt.axis('equal') + plt.title("Eigenvector demonstration") + plt.xlabel("x") + plt.ylabel("y") + plt.axis("equal") plt.xlim(-s, s) plt.ylim(-s, s) - ax.set_aspect('equal') + ax.set_aspect("equal") - l1, = plt.plot([0, 0], [0, 0], color='r', linewidth=3) # input vector - l2, = plt.plot([0, 0], [0, 0], color='b', linewidth=3) # transformed vector + (l1,) = plt.plot([0, 0], [0, 0], color="r", linewidth=3) # input vector + (l2,) = plt.plot([0, 0], [0, 0], color="b", linewidth=3) # transformed vector - plt.plot([0, x[0, 0]], [0, x[1, 0]], color='k', linewidth=1) - plt.plot([0, x[0, 1]], [0, x[1, 1]], color='k', linewidth=1) - + plt.plot([0, x[0, 0]], [0, x[1, 0]], color="k", linewidth=1) + plt.plot([0, x[0, 1]], [0, x[1, 1]], color="k", linewidth=1) - plt.legend(['$x$', r'${\bf A} x$', r'$x_1$', r'$x_2$']) + plt.legend(["$x$", r"${\bf A} x$", r"$x_1$", r"$x_2$"]) print("\nto exit: type q, or close the window") def animate(theta): - x = np.r_[cos(-theta), sin(-theta)] y = A @ x @@ -63,42 +61,42 @@ def animate(theta): return l1, l2 - myAnimation = animation.FuncAnimation( - fig, animate, frames=np.linspace( - 0, 2 * pi, 400), blit=True, interval=20, repeat=True) + fig, + animate, + frames=np.linspace(0, 2 * pi, 400), + blit=True, + interval=20, + repeat=True, + ) plt.show(block=True) -def main(): +def main(): def help(): print("eigdemo # uses default matrix [1 2; 3 4]") print("eigdemo a b c d # uses matrix [a b; c d]") sys.exit(0) - + if sys.argv in ("-h", "--help", "help"): help() if len(sys.argv) == 5: - try: vec = [float(a) for a in sys.argv[1:5]] - A = np.reshape(vec, (2,2)) + A = np.reshape(vec, (2, 2)) except: help() elif len(sys.argv) == 1: - A = np.array([ - [1, 2], - [3, 3] - ]) - + A = np.array([[1, 2], [3, 3]]) + else: help() - eigdemo(A) + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/roboticstoolbox/examples/fetch_vision.py b/roboticstoolbox/examples/fetch_vision.py index e6ebdcbfe..3cbd5a26e 100644 --- a/roboticstoolbox/examples/fetch_vision.py +++ b/roboticstoolbox/examples/fetch_vision.py @@ -67,7 +67,6 @@ def transform_between_vectors(a, b): def step(): - # Find end-effector pose in world frame wTe = fetch.fkine(fetch.q).A # Find camera pose in world frame diff --git a/roboticstoolbox/examples/holistic_mm_non_holonomic.py b/roboticstoolbox/examples/holistic_mm_non_holonomic.py index 6aa02735b..81f81ed08 100644 --- a/roboticstoolbox/examples/holistic_mm_non_holonomic.py +++ b/roboticstoolbox/examples/holistic_mm_non_holonomic.py @@ -13,7 +13,6 @@ def step_robot(r: rtb.ERobot, Tep): - wTe = r.fkine(r.q) eTep = np.linalg.inv(wTe) @ Tep @@ -112,7 +111,6 @@ def step_robot(r: rtb.ERobot, Tep): while not arrived: - arrived, frankie.qd = step_robot(frankie, wTep.A) env.step(dt) diff --git a/roboticstoolbox/examples/holistic_mm_omni.py b/roboticstoolbox/examples/holistic_mm_omni.py index 0444ff333..99ec985b0 100644 --- a/roboticstoolbox/examples/holistic_mm_omni.py +++ b/roboticstoolbox/examples/holistic_mm_omni.py @@ -13,7 +13,6 @@ def step_robot(r: rtb.ERobot, Tep): - wTe = r.fkine(r.q) eTep = np.linalg.inv(wTe) @ Tep @@ -112,7 +111,6 @@ def step_robot(r: rtb.ERobot, Tep): while not arrived: - arrived, frankie.qd = step_robot(frankie, wTep.A) env.step(dt) diff --git a/roboticstoolbox/examples/ik_exp.py b/roboticstoolbox/examples/ik_exp.py index bb2693bb6..3ae817c33 100644 --- a/roboticstoolbox/examples/ik_exp.py +++ b/roboticstoolbox/examples/ik_exp.py @@ -1,16 +1,7 @@ import numpy as np import roboticstoolbox as rtb -import spatialmath as sm -import fknm -import time -import swift -import spatialgeometry as sg -import sys from ansitable import ANSITable -from numpy import ndarray -from spatialmath import SE3 -from typing import Union, overload, List, Set # Our robot and ETS robot = rtb.models.Panda() @@ -47,7 +38,6 @@ class IK: def __init__(self, name, solve, problems=problems): - # Solver attributes self.name = name diff --git a/roboticstoolbox/examples/ik_speed.py b/roboticstoolbox/examples/ik_speed.py index b7d82f8ef..a254cdf02 100644 --- a/roboticstoolbox/examples/ik_speed.py +++ b/roboticstoolbox/examples/ik_speed.py @@ -1,16 +1,8 @@ import numpy as np import roboticstoolbox as rtb -import spatialmath as sm -import fknm import time -import swift -import spatialgeometry as sg -import sys from ansitable import ANSITable -from numpy import ndarray -from spatialmath import SE3 -from typing import Union, overload, List, Set # Our robot and ETS robot = rtb.models.Panda() diff --git a/roboticstoolbox/examples/ikine_evaluate.py b/roboticstoolbox/examples/ikine_evaluate.py index 4b87230d5..d4c4c2f80 100644 --- a/roboticstoolbox/examples/ikine_evaluate.py +++ b/roboticstoolbox/examples/ikine_evaluate.py @@ -9,15 +9,15 @@ # * T, the end-effector pose # * q0, the initial joint angles for solution -example = 'puma' # 'panda' +example = "puma" # 'panda' -if example == 'puma': +if example == "puma": # Puma robot case robot = rtb.models.DH.Puma560() q = robot.qn q0 = robot.qz T = robot.fkine(q) -elif example == 'panda': +elif example == "panda": # Panda robot case robot = rtb.models.DH.Panda() T = SE3(0.7, 0.2, 0.1) * SE3.OA([0, 1, 0], [0, 0, -1]) @@ -29,22 +29,28 @@ # - the method to execute # - the name for the results table # - the statement to execute for timeit -ikfuncs = [ - (robot.ikine_LM, # Levenberg-Marquadt +ikfuncs = [ + ( + robot.ikine_LM, # Levenberg-Marquadt "ikine_LM", - "sol = robot.ikine_LM(T, q0)" + "sol = robot.ikine_LM(T, q0)", ), - (robot.ikine_LMS, # Levenberg-Marquadt (Sugihara) + ( + robot.ikine_LMS, # Levenberg-Marquadt (Sugihara) "ikine_LMS", - "sol = robot.ikine_LMS(T, q0)" + "sol = robot.ikine_LMS(T, q0)", ), - (robot.ikine_min, #numerical solution with no constraints + ( + robot.ikine_min, # numerical solution with no constraints "ikine_min(qlim=False)", - "sol = robot.ikine_min(T, q0)" + "sol = robot.ikine_min(T, q0)", ), - (lambda T, q0: robot.ikine_min(T, q0, qlim=True), #numerical solution with constraints + ( + lambda T, q0: robot.ikine_min( + T, q0, qlim=True + ), # numerical solution with constraints "ikine_min(qlim=True)", - "sol = robot.ikine_min(T, q0, qlim=True)" + "sol = robot.ikine_min(T, q0, qlim=True)", ), # (robot.ikine_mmc, #numerical solution with no constraints # "ikine_min(qlim=False)", @@ -52,29 +58,31 @@ # ), ] if hasattr(robot, "ikine_a"): - a = (robot.ikine_a, # analytic solution + a = ( + robot.ikine_a, # analytic solution "ikine_a", - "sol = robot.ikine_a(T)" + "sol = robot.ikine_a(T)", ) - ikfuncs.insert(0, a) + ikfuncs.insert(0, a) # setup to run timeit -setup = ''' +setup = """ from __main__ import robot, T, q0 -''' +""" N = 10 # setup results table table = ANSITable( - Column("Operation", headalign="^", colalign='<'), + Column("Operation", headalign="^", colalign="<"), Column("Time (ms)", headalign="^", fmt="{:.2g}"), Column("Error", headalign="^", fmt="{:.3g}"), - border="thick") + border="thick", +) # test the IK methods for ik in ikfuncs: - print('Testing:', ik[1]) - + print("Testing:", ik[1]) + # test the method, don't pass q0 to the analytic function if ik[1] == "ikine_a": sol = ik[0](T) @@ -83,11 +91,11 @@ # print error message if there is one if not sol.success: - print(' failed:', sol.reason) + print(" failed:", sol.reason) # evalute the error err = np.linalg.norm(T - robot.fkine(sol.q)) - print(' error', err) + print(" error", err) if N > 0: # evaluate the execution time @@ -96,8 +104,8 @@ t = 0 # add it to the output table - table.row(f"`{ik[1]}`", t/N*1e3, err) + table.row(f"`{ik[1]}`", t / N * 1e3, err) -# pretty print the results +# pretty print the results table.print() -print(table.markdown()) \ No newline at end of file +print(table.markdown()) diff --git a/roboticstoolbox/examples/ikine_evaluate2.py b/roboticstoolbox/examples/ikine_evaluate2.py index 38f68ab61..732cf3eaa 100644 --- a/roboticstoolbox/examples/ikine_evaluate2.py +++ b/roboticstoolbox/examples/ikine_evaluate2.py @@ -3,87 +3,91 @@ import roboticstoolbox as rtb import timeit from ansitable import ANSITable, Column -import traceback # change for the robot IK under test, must set: # * robot, the DHRobot object # * T, the end-effector pose # * q0, the initial joint angles for solution -example = 'puma' # 'panda' +example = "puma" # 'panda' -if example == 'puma': +if example == "puma": # Puma robot case robot = rtb.models.DH.Puma560() q = robot.qn q0 = robot.qz T = robot.fkine(q) -elif example == 'panda': +elif example == "panda": # Panda robot case robot = rtb.models.DH.Panda() T = SE3(0.7, 0.2, 0.1) * SE3.OA([0, 1, 0], [0, 0, -1]) q0 = robot.qz solvers = [ - 'Nelder-Mead', - 'Powell', - 'CG', - 'BFGS', - 'Newton-CG', ## Jacobian is required - 'L-BFGS-B', - 'TNC', - 'COBYLA', - 'SLSQP', - 'trust-constr', - 'dogleg', - 'trust-ncg', - 'trust-exact', - 'trust-krylov', - ] + "Nelder-Mead", + "Powell", + "CG", + "BFGS", + "Newton-CG", ## Jacobian is required + "L-BFGS-B", + "TNC", + "COBYLA", + "SLSQP", + "trust-constr", + "dogleg", + "trust-ncg", + "trust-exact", + "trust-krylov", +] # setup to run timeit -setup = ''' +setup = """ from __main__ import robot, T, q0 -''' +""" N = 10 # setup results table table = ANSITable( - Column("Solver", headalign="^", colalign='<'), - Column("Time (ms)", headalign="^", fmt="{:.2g}", colalign='>'), - Column("Error", headalign="^", fmt="{:.3g}", colalign='>'), - border="thick") + Column("Solver", headalign="^", colalign="<"), + Column("Time (ms)", headalign="^", fmt="{:.2g}", colalign=">"), + Column("Error", headalign="^", fmt="{:.3g}", colalign=">"), + border="thick", +) # test the IK methods for solver in solvers: - print('Testing:', solver) - + print("Testing:", solver) + # test the method, don't pass q0 to the analytic function try: sol = robot.ikine_min(T, q0=q0, qlim=True, method=solver) except Exception as e: - print('***', solver, ' failed') + print("***", solver, " failed") print(e) continue # print error message if there is one if not sol.success: - print(' failed:', sol.reason) + print(" failed:", sol.reason) # evalute the error err = np.linalg.norm(T - robot.fkine(sol.q)) - print(' error', err) + print(" error", err) - if N > 0: # noqa + if N > 0: # noqa # evaluate the execution time - t = timeit.timeit(stmt=f"robot.ikine_min(T, q0=q0, qlim=True, method='{solver}')", setup=setup, number=N) + t = timeit.timeit( + stmt=f"robot.ikine_min(T, q0=q0, qlim=True, method='{solver}')", + setup=setup, + number=N, + ) else: t = 0 # add it to the output table - table.row(f"`{solver}`", t/N*1e3, err) + table.row(f"`{solver}`", t / N * 1e3, err) -# pretty print the results +# pretty print the results table.print() print(table.markdown()) diff --git a/roboticstoolbox/examples/mmc.py b/roboticstoolbox/examples/mmc.py index 653313a21..ab7fa94b4 100644 --- a/roboticstoolbox/examples/mmc.py +++ b/roboticstoolbox/examples/mmc.py @@ -37,7 +37,6 @@ arrived = False while not arrived: - # The pose of the Panda's end-effector Te = panda.fkine(panda.q) diff --git a/roboticstoolbox/examples/neo.py b/roboticstoolbox/examples/neo.py index a873cd93a..4adf42e9c 100644 --- a/roboticstoolbox/examples/neo.py +++ b/roboticstoolbox/examples/neo.py @@ -94,7 +94,6 @@ def step(): # For each collision in the scene for collision in collisions: - # Form the velocity damper inequality contraint for each collision # object on the robot to the collision in the scene c_Ain, c_bin = panda.link_collision_damper( diff --git a/roboticstoolbox/examples/park.py b/roboticstoolbox/examples/park.py index a5a722444..e4d0f547a 100644 --- a/roboticstoolbox/examples/park.py +++ b/roboticstoolbox/examples/park.py @@ -38,7 +38,6 @@ arrived = False while not arrived: - # The pose of the Panda's end-effector Te = panda.fkine(panda.q) @@ -57,9 +56,7 @@ # Project the gradient of manipulability into the null-space of the # differential kinematics - null = ( - np.eye(n) - np.linalg.pinv(Je) @ Je - ) @ Jm + null = (np.eye(n) - np.linalg.pinv(Je) @ Je) @ Jm # Solve for the joint velocities dq qd = np.linalg.pinv(Je) @ v + 1 / Y * null.flatten() diff --git a/roboticstoolbox/examples/plot.py b/roboticstoolbox/examples/plot.py index cbe7bfba3..b064de9d8 100644 --- a/roboticstoolbox/examples/plot.py +++ b/roboticstoolbox/examples/plot.py @@ -16,5 +16,5 @@ q = np.random.rand(100, 7) # Plot the joint trajectory with a 50ms delay between configurations -panda.plot(q=q, backend='pyplot', dt=0.050, vellipse=True, fellipse=True) +panda.plot(q=q, backend="pyplot", dt=0.050, vellipse=True, fellipse=True) # panda.plot(q=q, backend='swift', dt=0.050, vellipse=False, fellipse=False) diff --git a/roboticstoolbox/examples/readme.py b/roboticstoolbox/examples/readme.py index 4b83f8c0d..ddfc1f790 100644 --- a/roboticstoolbox/examples/readme.py +++ b/roboticstoolbox/examples/readme.py @@ -25,13 +25,13 @@ robot = rtb.models.URDF.Panda() print(robot) -env = swift.Swift() # instantiate 3D browser-based visualizer -env.launch("chrome") # activate it -env.add(robot) # add robot to the 3D scene +env = swift.Swift() # instantiate 3D browser-based visualizer +env.launch("chrome") # activate it +env.add(robot) # add robot to the 3D scene env.start_recording("panda2", 1 / dt) -for qk in qtraj.q: # for each joint configuration on trajectory - robot.q = qk # update the robot state - env.step() # update visualization +for qk in qtraj.q: # for each joint configuration on trajectory + robot.q = qk # update the robot state + env.step() # update visualization env.stop_recording() # ffmpeg -i panda2.webm -vf "scale=iw*.5:ih*.5" panda2.gif diff --git a/roboticstoolbox/examples/swift_recording.py b/roboticstoolbox/examples/swift_recording.py index 01ce8d7f2..cd8097976 100644 --- a/roboticstoolbox/examples/swift_recording.py +++ b/roboticstoolbox/examples/swift_recording.py @@ -19,7 +19,7 @@ env = swift.Swift() # Launch the sim in chrome as only chrome supports webm videos -env.launch('google-chrome') +env.launch("google-chrome") # Create a Panda robot object panda = rtb.models.Panda() @@ -36,7 +36,7 @@ # Start recording with a framerate of 1/dt and # call the video panda_swift_recording # Export video as a webm file (this only works in Chrome) -env.start_recording('panda_swift_recording', 1 / dt) +env.start_recording("panda_swift_recording", 1 / dt) # To export as a gif replace the above line with # env.start_recording('panda_swift_recording', 1 / dt, format='gif') @@ -56,7 +56,6 @@ arrived = False while not arrived: - # The pose of the Panda's end-effector Te = panda.fkine(panda.q) diff --git a/roboticstoolbox/examples/teach_swift.py b/roboticstoolbox/examples/teach_swift.py index 0fc844015..a75b45e36 100644 --- a/roboticstoolbox/examples/teach_swift.py +++ b/roboticstoolbox/examples/teach_swift.py @@ -29,7 +29,6 @@ def set_joint(j, value): j = 0 for link in panda.links: if link.isjoint: - # We use a lambda as the callback function from Swift # j=j is used to set the value of j rather than the variable j # We use the HTML unicode format for the degree sign in the unit arg diff --git a/roboticstoolbox/examples/test.py b/roboticstoolbox/examples/test.py index 1ef320ee8..85e7356f8 100644 --- a/roboticstoolbox/examples/test.py +++ b/roboticstoolbox/examples/test.py @@ -1,6 +1,5 @@ import numpy as np import roboticstoolbox as rtb -import spatialmath as sm q0 = np.array( [ diff --git a/roboticstoolbox/examples/tripleangledemo.py b/roboticstoolbox/examples/tripleangledemo.py index c15d58088..858174bc0 100644 --- a/roboticstoolbox/examples/tripleangledemo.py +++ b/roboticstoolbox/examples/tripleangledemo.py @@ -37,7 +37,7 @@ g3 = Mesh( filename=str(path / "gimbal-ring3.stl"), color=[240, 103, 103], - scale=(1.1 ** 2 / 3,) * 3, + scale=(1.1**2 / 3,) * 3, ) plane = Mesh( diff --git a/roboticstoolbox/examples/twistdemo.py b/roboticstoolbox/examples/twistdemo.py index a5c65c41c..83f50db78 100644 --- a/roboticstoolbox/examples/twistdemo.py +++ b/roboticstoolbox/examples/twistdemo.py @@ -3,6 +3,7 @@ @author Peter Corke @author Jesse Haviland """ + import sys import swift from math import pi @@ -29,17 +30,18 @@ ) print("Supermarine Spitfire Mk VIII by Ed Morley @GRABCAD") -screw = Cylinder(radius=0.015, length=4, color='blue', collision=False) +screw = Cylinder(radius=0.015, length=4, color="blue", collision=False) -BASE = SE3(0.15, 0, 0.1) * SE3.Rz(pi/2) * SE3.Rx(np.pi/2) +BASE = SE3(0.15, 0, 0.1) * SE3.Rz(pi / 2) * SE3.Rx(np.pi / 2) env.add(plane) env.add(screw) -twist_params = {'x':0, 'y': 0, 'R': 0, 'P':0, 'p': 0.1} +twist_params = {"x": 0, "y": 0, "R": 0, "P": 0, "p": 0.1} twist = Twist3() + def update_twist_param(value, key): global twist_params @@ -47,69 +49,101 @@ def update_twist_param(value, key): update_twist() + def update_twist(): global twist - axis = SO3.Rx(twist_params['R']) @ SO3.Ry(twist_params['P']) - point = [twist_params['x'], twist_params['y'], 0] - twist = Twist3.UnitRevolute(axis.A[:, 2], point, twist_params['p']) + axis = SO3.Rx(twist_params["R"]) @ SO3.Ry(twist_params["P"]) + point = [twist_params["x"], twist_params["y"], 0] + twist = Twist3.UnitRevolute(axis.A[:, 2], point, twist_params["p"]) update_screw_axis(axis, point) update_plane() + # button to reset twist def reset_twist(e): global twist_params - twist_params['x'] = 0 - twist_params['y'] = 0 - twist_params['R'] = 0 - twist_params['P'] = 0 - twist_params['p'] = 0 + twist_params["x"] = 0 + twist_params["y"] = 0 + twist_params["R"] = 0 + twist_params["P"] = 0 + twist_params["p"] = 0 update_all_sliders() update_twist() update_plane() # env.step(0) -def update_plane(*args): +def update_plane(*args): T = twist.SE3(twist_theta.value) * BASE plane.T = T + def update_screw_axis(axis, point): screw.T = SE3.Rt(axis, point) + def update_all_sliders(): - twist_x.value = twist_params['x'] - twist_y.value = twist_params['y'] - twist_rollangle.value = twist_params['R'] - twist_pitchangle.value = twist_params['P'] - twist_pitch.value = twist_params['p'] + twist_x.value = twist_params["x"] + twist_y.value = twist_params["y"] + twist_rollangle.value = twist_params["R"] + twist_pitchangle.value = twist_params["P"] + twist_pitch.value = twist_params["p"] twist_x = swift.Slider( - lambda x: update_twist_param(x, 'x'), min=-1, max=1, step=0.01, value=0, desc="x position" + lambda x: update_twist_param(x, "x"), + min=-1, + max=1, + step=0.01, + value=0, + desc="x position", ) twist_y = swift.Slider( - lambda y: update_twist_param(y, 'y'), min=-1, max=1, step=0.01, value=0, desc="y position" + lambda y: update_twist_param(y, "y"), + min=-1, + max=1, + step=0.01, + value=0, + desc="y position", ) twist_rollangle = swift.Slider( - lambda r: update_twist_param(np.deg2rad(r), 'R'), min=-180, max=180, step=1, value=0, desc="roll angle (rx)", unit="°" + lambda r: update_twist_param(np.deg2rad(r), "R"), + min=-180, + max=180, + step=1, + value=0, + desc="roll angle (rx)", + unit="°", ) twist_pitchangle = swift.Slider( - lambda p: update_twist_param(np.deg2rad(p), 'P'), min=-180, max=180, step=1, value=0, desc="pitch angle (ry)", unit="°" + lambda p: update_twist_param(np.deg2rad(p), "P"), + min=-180, + max=180, + step=1, + value=0, + desc="pitch angle (ry)", + unit="°", ) twist_pitch = swift.Slider( - lambda p: update_twist_param(p, 'p'), min=0, max=1, step=0.02, value=0, desc="screw pitch" + lambda p: update_twist_param(p, "p"), + min=0, + max=1, + step=0.02, + value=0, + desc="screw pitch", ) twist_theta = swift.Slider( - update_plane, min=0, max=10, step=0.02, value=0, desc="Twist rotation") + update_plane, min=0, max=10, step=0.02, value=0, desc="Twist rotation" +) button = swift.Button(reset_twist, desc="Set to Zero") quit = swift.Button(lambda x: sys.exit(0), desc="Quit") diff --git a/roboticstoolbox/examples/vehicle1.py b/roboticstoolbox/examples/vehicle1.py index 395de5dca..1aca7b289 100644 --- a/roboticstoolbox/examples/vehicle1.py +++ b/roboticstoolbox/examples/vehicle1.py @@ -1,7 +1,6 @@ # from roboticstoolbox import Bicycle, RandomPath, VehiclePolygon from roboticstoolbox import VehicleIcon from spatialmath import * # lgtm [py/polluting-import] -from math import pi dim = 10 diff --git a/roboticstoolbox/mobile/Animations.py b/roboticstoolbox/mobile/Animations.py index 86d545e15..8622dd79e 100644 --- a/roboticstoolbox/mobile/Animations.py +++ b/roboticstoolbox/mobile/Animations.py @@ -2,6 +2,7 @@ @Author: Peter Corke, original MATLAB code and Python version @Author: Kristian Gibson, initial MATLAB port """ + from abc import ABC from math import pi, atan2 import numpy as np @@ -112,7 +113,6 @@ def plot(self, q, **kwargs): return self._object def __del__(self): - if self._object is not None: self._object.remove() @@ -204,7 +204,6 @@ def __init__(self, shape="car", scale=1, **kwargs): """ super().__init__() if isinstance(shape, str): - h = 0.3 t = 0.8 # start of head taper c = 0.5 # centre x coordinate @@ -247,7 +246,6 @@ def _add(self, **kwargs): self._ax.add_patch(self._object) def _update(self, x): - if self._object is not None: # if animation is initialized xy = SE2(x) * self._coords @@ -398,7 +396,6 @@ def imshow_affine(ax, z, *args, **kwargs): ) def _update(self, x): - # center_x = self._width // 2 # center_y = self._height // 2 center_x = 0 diff --git a/roboticstoolbox/mobile/DistanceTransformPlanner.py b/roboticstoolbox/mobile/DistanceTransformPlanner.py index d14995f50..90c108a9c 100644 --- a/roboticstoolbox/mobile/DistanceTransformPlanner.py +++ b/roboticstoolbox/mobile/DistanceTransformPlanner.py @@ -2,11 +2,9 @@ @Author: Peter Corke, original MATLAB code and Python version @Author: Kristian Gibson, initial MATLAB port """ -from numpy import disp -from scipy import integrate + from spatialmath.base.transforms2d import * from spatialmath.base.vectors import * -from spatialmath.pose2d import SE2 from spatialmath import base from scipy.ndimage import * import matplotlib.pyplot as plt @@ -67,7 +65,6 @@ class DistanceTransformPlanner(PlannerBase): """ def __init__(self, occgrid=None, metric="euclidean", **kwargs): - super().__init__(occgrid=occgrid, ndims=2, **kwargs) self._metric = metric self._distancemap = None @@ -221,7 +218,11 @@ def plot_3d(self, path=None, ls=None): distance = self._distancemap X, Y = np.meshgrid(np.arange(distance.shape[1]), np.arange(distance.shape[0])) surf = ax.plot_surface( - X, Y, distance, linewidth=1, antialiased=False # cmap='gray', + X, + Y, + distance, + linewidth=1, + antialiased=False, # cmap='gray', ) if path is not None: @@ -345,7 +346,6 @@ def distancexform(occgrid, goal, metric="cityblock", animate=False, summary=Fals def grassfire_step(G, D): - # pad with inf H = np.pad(G, max(D.shape) // 2, "constant", constant_values=np.inf) rows, columns = G.shape diff --git a/roboticstoolbox/mobile/DstarPlanner.py b/roboticstoolbox/mobile/DstarPlanner.py index 334dfabf0..ec61ceb4b 100644 --- a/roboticstoolbox/mobile/DstarPlanner.py +++ b/roboticstoolbox/mobile/DstarPlanner.py @@ -5,19 +5,16 @@ See Wikipedia article (https://en.wikipedia.org/wiki/D*) """ + import math -from sys import maxsize from collections import namedtuple from enum import IntEnum, auto import matplotlib.pyplot as plt import numpy as np -import matplotlib.cm as cm from roboticstoolbox.mobile.PlannerBase import PlannerBase -from roboticstoolbox.mobile.OccGrid import BinaryOccupancyGrid, OccupancyGrid +from roboticstoolbox.mobile.OccGrid import OccupancyGrid import heapq -import bisect -import math show_animation = True @@ -310,6 +307,7 @@ def showparents(self): # ====================== RTB wrapper ============================= # + # Copyright (c) 2022 Peter Corke: https://github.com/petercorke/robotics-toolbox-python # Released under the MIT license: https://github.com/AtsushiSakai/PythonRobotics/blob/master/LICENSE class DstarPlanner(PlannerBase): @@ -520,7 +518,6 @@ def query(self, start, sensor=None, animate=False, verbose=False): if __name__ == "__main__": - og = np.zeros((10, 10)) og[4:8, 3:6] = 1 print(og) diff --git a/roboticstoolbox/mobile/DubinsPlanner.py b/roboticstoolbox/mobile/DubinsPlanner.py index ec6cef47e..8ff473b90 100644 --- a/roboticstoolbox/mobile/DubinsPlanner.py +++ b/roboticstoolbox/mobile/DubinsPlanner.py @@ -232,7 +232,7 @@ def generate_local_course(total_length, lengths, mode, max_curvature, step_size) ll = 0.0 - for (m, l, i) in zip(mode, lengths, range(len(mode))): + for m, l, i in zip(mode, lengths, range(len(mode))): if l > 0.0: d = step_size else: @@ -334,6 +334,7 @@ def path_planning(start, goal, curvature, step_size=0.1): # ====================== RTB wrapper ============================= # + # Copyright (c) 2022 Peter Corke: https://github.com/petercorke/robotics-toolbox-python # Released under the MIT license: https://github.com/AtsushiSakai/PythonRobotics/blob/master/LICENSE class DubinsPlanner(PlannerBase): @@ -381,7 +382,6 @@ class DubinsPlanner(PlannerBase): """ def __init__(self, curvature=1, stepsize=0.1, **kwargs): - super().__init__(ndims=3, **kwargs) self._curvature = curvature self._stepsize = stepsize diff --git a/roboticstoolbox/mobile/EKF.py b/roboticstoolbox/mobile/EKF.py index bc9129f4c..2a3bc555c 100644 --- a/roboticstoolbox/mobile/EKF.py +++ b/roboticstoolbox/mobile/EKF.py @@ -6,14 +6,11 @@ from collections import namedtuple import numpy as np -from math import pi -from scipy import integrate -from scipy.linalg import sqrtm, block_diag +from scipy.linalg import block_diag from scipy.stats.distributions import chi2 import matplotlib.pyplot as plt from matplotlib import animation -from spatialmath.base.animate import Animate from spatialmath import base, SE2 from roboticstoolbox.mobile import VehicleBase from roboticstoolbox.mobile.landmarkmap import LandmarkMap @@ -991,7 +988,6 @@ def step(self, pause=None): ## landmark management def _isseenbefore(self, lm_id): - # _landmarks[0, id] is the order in which seen # _landmarks[1, id] is the occurence count @@ -1424,7 +1420,6 @@ def get_transform(self, map): if __name__ == "__main__": - from roboticstoolbox import * V = np.diag([0.02, np.deg2rad(0.5)]) ** 2 diff --git a/roboticstoolbox/mobile/LatticePlanner.py b/roboticstoolbox/mobile/LatticePlanner.py index 64a528f7c..cca9eb2cc 100644 --- a/roboticstoolbox/mobile/LatticePlanner.py +++ b/roboticstoolbox/mobile/LatticePlanner.py @@ -2,9 +2,7 @@ import numpy as np from spatialmath import SE2 import matplotlib.pyplot as plt -import itertools from roboticstoolbox.mobile.PlannerBase import PlannerBase -from roboticstoolbox.mobile.OccGrid import BinaryOccupancyGrid from collections import namedtuple @@ -135,7 +133,6 @@ class LatticePlanner(PlannerBase): """ def __init__(self, costs=None, root=(0, 0, 0), **kwargs): - global arcs super().__init__(ndims=3, **kwargs) @@ -191,7 +188,6 @@ def plan(self, iterations=None, verbose=False, summary=False): iteration = 0 while True: - newfrontier = [] for vertex in frontier: if verbose: @@ -310,7 +306,6 @@ def plot(self, path=None, **kwargs): super().plot(**kwargs) if kwargs.get("configspace", False): - # 3D plot for k, vertex in enumerate(self.graph): # for every node @@ -359,7 +354,6 @@ def plot(self, path=None, **kwargs): if __name__ == "__main__": - lattice = LatticePlanner() lattice.plan(iterations=6) path = lattice.query(start=(0, 0, np.pi / 2), goal=(1, 1, 0)) diff --git a/roboticstoolbox/mobile/OccGrid.py b/roboticstoolbox/mobile/OccGrid.py index f09a9cc67..27b0c3472 100644 --- a/roboticstoolbox/mobile/OccGrid.py +++ b/roboticstoolbox/mobile/OccGrid.py @@ -1,6 +1,5 @@ import numpy as np import matplotlib.pyplot as plt -from spatialmath import base import scipy.ndimage as sp from abc import ABC import spatialmath.base as smb @@ -316,7 +315,6 @@ def line_w(self, p1, p2): return self._line(gp1, gp2) def _line(self, p1, p2): - x, y = smb.bresenham(p1, p2) z = np.ravel_multi_index(np.vstack((y, x)), self.grid.shape) return z @@ -563,7 +561,6 @@ def workspace(self): if __name__ == "__main__": - # g = np.zeros((100, 100)) # g[20:30, 50:80] = 1 diff --git a/roboticstoolbox/mobile/PRMPlanner.py b/roboticstoolbox/mobile/PRMPlanner.py index d2ccc6d39..efba2d9f4 100644 --- a/roboticstoolbox/mobile/PRMPlanner.py +++ b/roboticstoolbox/mobile/PRMPlanner.py @@ -3,6 +3,7 @@ @Author: Peter Corke, original MATLAB code and Python version @Author: Kristian Gibson, initial MATLAB port """ + # from multiprocessing.sharedctypes import Value # from numpy import disp # from scipy import integrate @@ -13,7 +14,7 @@ # from spatialmath.pose2d import SE2 # from spatialmath.base import animate from scipy.ndimage import * -from matplotlib import cm, pyplot as plt +from matplotlib import pyplot as plt from roboticstoolbox.mobile.PlannerBase import PlannerBase from pgraph import UGraph diff --git a/roboticstoolbox/mobile/ParticleFilter.py b/roboticstoolbox/mobile/ParticleFilter.py index 8744a1bea..5b345a28c 100644 --- a/roboticstoolbox/mobile/ParticleFilter.py +++ b/roboticstoolbox/mobile/ParticleFilter.py @@ -5,7 +5,7 @@ @Author: Peter Corke, original MATLAB code and Python version @Author: Kristian Gibson, initial MATLAB port -Based on code by Paul Newman, Oxford University, +Based on code by Paul Newman, Oxford University, http://www.robots.ox.ac.uk/~pnewman """ @@ -355,7 +355,6 @@ def run(self, T=10, x0=None): # self.robot.plot() # iterate over time - import time for i in range(round(T / self.robot.dt)): self._step() @@ -491,7 +490,6 @@ def run_animation(self, T=10, x0=None, format=None, file=None): # # anim.close() def _step(self): - # fprintf('---- step\n') odo = self.robot.step(animate=self._animate) # move the robot diff --git a/roboticstoolbox/mobile/PlannerBase.py b/roboticstoolbox/mobile/PlannerBase.py index d68487799..aae219cad 100644 --- a/roboticstoolbox/mobile/PlannerBase.py +++ b/roboticstoolbox/mobile/PlannerBase.py @@ -15,7 +15,6 @@ # from spatialmath import SE2, SE3 from matplotlib import cm -from abc import ABC import matplotlib as mpl import matplotlib.pyplot as plt import copy @@ -86,7 +85,6 @@ def __init__( seed=None, **unused, ): - self._occgrid = None if ndims is None: raise ValueError("ndims must be specified") @@ -751,7 +749,6 @@ def plot( if start is not None: ax.plot(start[0], start[1], start[2], label="start", **start_marker) if goal is not None: - if path is not None and unwrap: theta = theta[-1] else: diff --git a/roboticstoolbox/mobile/PoseGraph.py b/roboticstoolbox/mobile/PoseGraph.py index 58c0c3384..cdf83bd01 100644 --- a/roboticstoolbox/mobile/PoseGraph.py +++ b/roboticstoolbox/mobile/PoseGraph.py @@ -15,7 +15,6 @@ class PGVertex(pgraph.UVertex): - nvertices = 0 # reset this before each PGGraph build def __init__(self, type, **kwargs): @@ -92,7 +91,6 @@ def linear_factors(self): class PoseGraph: - # properties # graph @@ -141,7 +139,6 @@ def __init__(self, filename, lidar=False, verbose=False): opener = open with opener(path, "r") as f: - toroformat = False nlidar = 0 @@ -301,7 +298,6 @@ def scan(self, i): return v.range, v.theta def scanxy(self, i): - range, theta = self.scan(i) range = np.where(range < self._maxrange, range, np.nan) x = range * np.cos(theta) @@ -323,11 +319,11 @@ def time(self, i): return self.vindex[i].time def plot(self, **kwargs): - if not "vopt" in kwargs: + if "vopt" not in kwargs: kwargs["vopt"] = dict( markersize=8, markerfacecolor="blue", markeredgecolor="None" ) - if not "eopt" in kwargs: + if "eopt" not in kwargs: kwargs["eopt"] = dict(linewidth=1) self.graph.plot(colorcomponents=False, force2d=True, **kwargs) plt.xlabel("x") @@ -349,7 +345,6 @@ def scanmap(self, occgrid, maxrange=None): grid1d = occgrid.ravel for i in range(0, self.graph.n, 5): - xy = self.scanxy(i) r, theta = self.scan(i) if maxrange is not None: @@ -363,7 +358,6 @@ def scanmap(self, occgrid, maxrange=None): p1 = occgrid.w2g(xyt[:2]) for p2 in occgrid.w2g(xy.T): - # all cells along the ray try: k = occgrid._line(p1, p2) @@ -389,7 +383,6 @@ def g2w(self, g): return (np.r_[g] - self._ngrid / 2) * self._cellsize + self._centre def plot_occgrid(self, w, block=None): - bl = self.g2w([0, 0]) tr = self.g2w([w.shape[1], w.shape[0]]) @@ -414,7 +407,6 @@ def plot_occgrid(self, w, block=None): # vol. 2, no. 4, pp. 31-43, winter 2010, doi: 10.1109/MITS.2010.939925. def optimize(self, iterations=10, animate=False, retain=True, **kwargs): - eprev = math.inf eo = {} @@ -523,7 +515,6 @@ def linearize_and_solve(self): if __name__ == "__main__": - pg = PoseGraph("data/pg1.g2o") pg.optimize(animate=True, retain=False) diff --git a/roboticstoolbox/mobile/QuinticPolyPlanner.py b/roboticstoolbox/mobile/QuinticPolyPlanner.py index 33b8e61c8..81ae1222d 100644 --- a/roboticstoolbox/mobile/QuinticPolyPlanner.py +++ b/roboticstoolbox/mobile/QuinticPolyPlanner.py @@ -72,12 +72,7 @@ def calc_first_derivative(self, t): return xt def calc_second_derivative(self, t): - xt = ( - 2 * self.a2 - + 6 * self.a3 * t - + 12 * self.a4 * t**2 - + 20 * self.a5 * t**3 - ) + xt = 2 * self.a2 + 6 * self.a3 * t + 12 * self.a4 * t**2 + 20 * self.a5 * t**3 return xt @@ -172,6 +167,7 @@ def quintic_polynomials_planner( # ====================== RTB wrapper ============================= # + # Copyright (c) 2022 Peter Corke: https://github.com/petercorke/robotics-toolbox-python # Released under the MIT license: https://github.com/AtsushiSakai/PythonRobotics/blob/master/LICENSE class QuinticPolyPlanner(PlannerBase): @@ -257,7 +253,6 @@ def __init__( min_t=5, max_t=100, ): - super().__init__(ndims=3) self.dt = dt self.start_vel = start_vel @@ -322,8 +317,6 @@ def query(self, start, goal): if __name__ == "__main__": - from math import pi - start = (10, 10, np.deg2rad(10.0)) goal = (30, -10, np.deg2rad(20.0)) diff --git a/roboticstoolbox/mobile/RRTPlanner.py b/roboticstoolbox/mobile/RRTPlanner.py index ec76179a4..6a1d19586 100644 --- a/roboticstoolbox/mobile/RRTPlanner.py +++ b/roboticstoolbox/mobile/RRTPlanner.py @@ -7,7 +7,6 @@ # Copyright (c) 2016 - 2022 Atsushi Sakai and other contributors: https://github.com/AtsushiSakai/PythonRobotics/contributors # Released under the MIT license: https://github.com/AtsushiSakai/PythonRobotics/blob/master/LICENSE -import math from collections import namedtuple from roboticstoolbox.mobile.OccGrid import PolygonMap @@ -16,7 +15,7 @@ import numpy as np import matplotlib.pyplot as plt -from spatialmath import Polygon2, SE2, base +from spatialmath import Polygon2 from roboticstoolbox.mobile.PlannerBase import PlannerBase from roboticstoolbox.mobile.DubinsPlanner import DubinsPlanner diff --git a/roboticstoolbox/mobile/ReedsSheppPlanner.py b/roboticstoolbox/mobile/ReedsSheppPlanner.py index 51c8aae91..7b25fc0e0 100644 --- a/roboticstoolbox/mobile/ReedsSheppPlanner.py +++ b/roboticstoolbox/mobile/ReedsSheppPlanner.py @@ -40,7 +40,7 @@ def plot_arrow(x, y, yaw, length=1.0, width=0.5, fc="r", ec="k"): """ if not isinstance(x, float): - for (ix, iy, iyaw) in zip(x, y, yaw): + for ix, iy, iyaw in zip(x, y, yaw): plot_arrow(ix, iy, iyaw) else: plt.arrow( @@ -316,7 +316,7 @@ def generate_local_course(total_length, lengths, mode, max_curvature, step_size) ll = 0.0 - for (m, l, i) in zip(mode, lengths, range(len(mode))): + for m, l, i in zip(mode, lengths, range(len(mode))): if l > 0.0: d = step_size else: @@ -389,7 +389,6 @@ def calc_paths(sx, sy, syaw, gx, gy, gyaw, maxc, step_size): def reeds_shepp_path_planning(start, goal, maxc, step_size): - s_x, s_y, s_yaw = start g_x, g_y, g_yaw = goal @@ -412,6 +411,7 @@ def reeds_shepp_path_planning(start, goal, maxc, step_size): # ====================== RTB wrapper ============================= # + # Copyright (c) 2022 Peter Corke: https://github.com/petercorke/robotics-toolbox-python # Released under the MIT license: https://github.com/AtsushiSakai/PythonRobotics/blob/master/LICENSE class ReedsSheppPlanner(PlannerBase): diff --git a/roboticstoolbox/mobile/Vehicle.py b/roboticstoolbox/mobile/Vehicle.py index 95c926a79..a2ccf7272 100644 --- a/roboticstoolbox/mobile/Vehicle.py +++ b/roboticstoolbox/mobile/Vehicle.py @@ -3,6 +3,7 @@ @Author: Peter Corke, original MATLAB code and Python version @Author: Kristian Gibson, initial MATLAB port """ + from abc import ABC, abstractmethod import warnings from math import pi, sin, cos, tan @@ -284,7 +285,6 @@ def control(self): @control.setter def control(self, control): - # * a function called as ``f(vehicle, t, x)`` that returns a tuple # * an interpolator called as f(t) that returns a tuple, see # SciPy interp1d @@ -610,7 +610,6 @@ def init(self, x0=None, control=None, animate=True): # initialize the graphics if animate and self._animation is not None: - # setup the plot self._ax = base.plotvol2(self.workspace) @@ -973,7 +972,6 @@ def __init__(self, L=1, steer_max=0.45 * pi, **kwargs): self._steer_max = steer_max def __str__(self): - s = super().__str__() s += ( f"\n L={self._l}, steer_max={self._steer_max:g}," @@ -1125,7 +1123,6 @@ def __init__(self, W=1, steer_max=np.inf, **kwargs): self._steer_max = steer_max def __str__(self): - s = super().__str__() s += ( f"\n W={self._W}, steer_max={self._steer_max:g}," @@ -1218,7 +1215,6 @@ def __init__(self, W=1, **kwargs): self._v_prev_R = [0] def __str__(self): - s = super().__str__() return s @@ -1227,7 +1223,6 @@ def init(self): self._v_prev_L = [0] self._v_prev_R = [0] - def u_limited(self, u): """ Apply vehicle velocity and acceleration limits @@ -1247,7 +1242,7 @@ def u_limited(self, u): ulim[1] = self.limits_va(u[1], self._v_prev_R) return ulim - + def deriv(self, x, u, limits=True): r""" Time derivative of state @@ -1292,11 +1287,7 @@ def deriv(self, x, u, limits=True): return np.r_[v * cos(theta), v * sin(theta), vdiff / self._W] - if __name__ == "__main__": - - from roboticstoolbox import RandomPath - import roboticstoolbox as rtb a = rtb.VehicleMarker(marker="s", markerfacecolor="b") diff --git a/roboticstoolbox/mobile/drivers.py b/roboticstoolbox/mobile/drivers.py index 0d2e0df70..33e35e05e 100644 --- a/roboticstoolbox/mobile/drivers.py +++ b/roboticstoolbox/mobile/drivers.py @@ -3,18 +3,14 @@ @Author: Peter Corke, original MATLAB code and Python version @Author: Kristian Gibson, initial MATLAB port """ + from abc import ABC, abstractmethod -import warnings -from math import pi, sin, cos, tan, atan2 +from math import atan2 import numpy as np -from scipy import integrate, linalg, interpolate import matplotlib.pyplot as plt -from matplotlib import patches -import matplotlib.transforms as mtransforms -from spatialmath import SE2, base -import roboticstoolbox as rtb +from spatialmath import base class VehicleDriverBase(ABC): @@ -66,7 +62,6 @@ def vehicle(self): @vehicle.setter def vehicle(self, v): - self._veh = v def __repr__(self): @@ -252,7 +247,6 @@ def demand(self): ## private method, invoked from demand() to compute a new waypoint def _new_goal(self): - # choose a uniform random goal within inner 80% of driving area while True: r = self._random.uniform(0.1, 0.9) @@ -296,5 +290,4 @@ def demand(self): # ========================================================================= # if __name__ == "__main__": - - import unittest + pass diff --git a/roboticstoolbox/mobile/landmarkmap.py b/roboticstoolbox/mobile/landmarkmap.py index 12bef3a56..73dc5404d 100644 --- a/roboticstoolbox/mobile/landmarkmap.py +++ b/roboticstoolbox/mobile/landmarkmap.py @@ -1,10 +1,6 @@ -from abc import ABC import numpy as np -import scipy as sp -from math import pi, sin, cos import matplotlib.pyplot as plt from spatialmath import base -import roboticstoolbox as rtb class LandmarkMap: @@ -59,7 +55,6 @@ class LandmarkMap: """ def __init__(self, map, workspace=10, verbose=True, seed=0): - try: self._workspace = workspace.workspace except: @@ -178,4 +173,4 @@ def plot(self, labels=False, block=None, **kwargs): if __name__ == "__main__": - import unittest + pass diff --git a/roboticstoolbox/mobile/sensors.py b/roboticstoolbox/mobile/sensors.py index ba0936df7..4b508769e 100644 --- a/roboticstoolbox/mobile/sensors.py +++ b/roboticstoolbox/mobile/sensors.py @@ -1,10 +1,7 @@ from abc import ABC import numpy as np -import scipy as sp from math import pi, sin, cos -import matplotlib.pyplot as plt from spatialmath import base -import roboticstoolbox as rtb from collections.abc import Iterable """ @@ -204,6 +201,7 @@ def plot(self, id): # ======================================================================== # + # visibility function, for one id, or return list of visible # covar can be 2x2 or (2,) # .W property @@ -221,7 +219,6 @@ def __init__( seed=0, **kwargs, ): - r""" Range and bearing angle sensor @@ -759,7 +756,6 @@ def Gz(self, x, z): if __name__ == "__main__": - from roboticstoolbox import Bicycle, LandmarkMap, RangeBearingSensor from math import pi diff --git a/roboticstoolbox/models/DH/AL5D.py b/roboticstoolbox/models/DH/AL5D.py index f4c42c745..565f38b2d 100644 --- a/roboticstoolbox/models/DH/AL5D.py +++ b/roboticstoolbox/models/DH/AL5D.py @@ -39,7 +39,6 @@ class AL5D(DHRobot): """ # noqa def __init__(self, symbolic=False): - if symbolic: import spatialmath.base.symbolic as sym @@ -116,7 +115,6 @@ def __init__(self, symbolic=False): if __name__ == "__main__": # pragma nocover - al5d = AL5D(symbolic=False) print(al5d) # print(al5d.dyntable()) diff --git a/roboticstoolbox/models/DH/Ball.py b/roboticstoolbox/models/DH/Ball.py index c2f9e37da..40a4a7f7d 100755 --- a/roboticstoolbox/models/DH/Ball.py +++ b/roboticstoolbox/models/DH/Ball.py @@ -50,7 +50,6 @@ class Ball(DHRobot): """ def __init__(self, N=10): - links = [] q1 = [] @@ -83,6 +82,5 @@ def _fract(i): if __name__ == "__main__": # pragma nocover - ball = Ball() print(ball) diff --git a/roboticstoolbox/models/DH/Baxter.py b/roboticstoolbox/models/DH/Baxter.py index 4e19c12db..a3f98d6df 100644 --- a/roboticstoolbox/models/DH/Baxter.py +++ b/roboticstoolbox/models/DH/Baxter.py @@ -45,7 +45,6 @@ class Baxter(DHRobot): """ # noqa def __init__(self, arm="left"): - links = [ RevoluteDH(d=0.27, a=0.069, alpha=-pi / 2), RevoluteDH(d=0, a=0, alpha=pi / 2, offset=pi / 2), @@ -81,7 +80,6 @@ def __init__(self, arm="left"): if __name__ == "__main__": # pragma nocover - baxter = Baxter("left") print(baxter.name, baxter.base) # baxter.plot(baxter.qz, block=False) diff --git a/roboticstoolbox/models/DH/Coil.py b/roboticstoolbox/models/DH/Coil.py index 7a5ff2b5f..97e67959b 100644 --- a/roboticstoolbox/models/DH/Coil.py +++ b/roboticstoolbox/models/DH/Coil.py @@ -47,7 +47,6 @@ class Coil(DHRobot): """ # noqa def __init__(self, N=10, symbolic=False): - if symbolic: import spatialmath.base.symbolic as sym @@ -73,7 +72,6 @@ def __init__(self, N=10, symbolic=False): if __name__ == "__main__": # pragma nocover - coil = Coil(N=10, symbolic=False) print(coil) diff --git a/roboticstoolbox/models/DH/Hyper.py b/roboticstoolbox/models/DH/Hyper.py index edab3b920..343381bac 100644 --- a/roboticstoolbox/models/DH/Hyper.py +++ b/roboticstoolbox/models/DH/Hyper.py @@ -3,7 +3,6 @@ @author: Peter Corke """ - # from math import pi import numpy as np from roboticstoolbox import DHRobot, RevoluteDH @@ -48,15 +47,12 @@ class Hyper(DHRobot): """ # noqa def __init__(self, N=10, a=None, symbolic=False): - if symbolic: import spatialmath.base.symbolic as sym zero = sym.zero() pi = sym.pi() else: - from math import pi - zero = 0.0 if a is None: @@ -78,7 +74,6 @@ def __init__(self, N=10, a=None, symbolic=False): if __name__ == "__main__": # pragma nocover - hyper = Hyper(N=10, symbolic=False) print(hyper) diff --git a/roboticstoolbox/models/DH/Hyper3d.py b/roboticstoolbox/models/DH/Hyper3d.py index 5bd899d71..9ee35706d 100644 --- a/roboticstoolbox/models/DH/Hyper3d.py +++ b/roboticstoolbox/models/DH/Hyper3d.py @@ -47,7 +47,6 @@ class Hyper3d(DHRobot): """ # noqa def __init__(self, N=10, a=None, symbolic=False): - if symbolic: import spatialmath.base.symbolic as sym @@ -77,7 +76,6 @@ def __init__(self, N=10, a=None, symbolic=False): if __name__ == "__main__": # pragma nocover - hyper = Hyper3d(N=10, symbolic=False) print(hyper) diff --git a/roboticstoolbox/models/DH/IRB140.py b/roboticstoolbox/models/DH/IRB140.py index 7572c0784..5aa3e75c4 100644 --- a/roboticstoolbox/models/DH/IRB140.py +++ b/roboticstoolbox/models/DH/IRB140.py @@ -121,10 +121,16 @@ def __init__(self): qlim=[-220 * deg, 60 * deg], ), RevoluteDH( - d=d4, a=0, alpha=pi / 2, qlim=[-200 * deg, 200 * deg] # alpha=-pi/2, + d=d4, + a=0, + alpha=pi / 2, + qlim=[-200 * deg, 200 * deg], # alpha=-pi/2, ), RevoluteDH( - d=0, a=0, alpha=-pi / 2, qlim=[-120 * deg, 120 * deg] # alpha=pi/2, + d=0, + a=0, + alpha=-pi / 2, + qlim=[-120 * deg, 120 * deg], # alpha=pi/2, ), RevoluteDH(d=d6, a=0, alpha=0, qlim=[-400 * deg, 400 * deg]), # alpha=pi/2, ] @@ -149,6 +155,5 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - robot = IRB140() print(robot) diff --git a/roboticstoolbox/models/DH/Jaco.py b/roboticstoolbox/models/DH/Jaco.py index 0219bd65a..73f84df54 100644 --- a/roboticstoolbox/models/DH/Jaco.py +++ b/roboticstoolbox/models/DH/Jaco.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -from math import sin, cos +from math import sin import numpy as np from roboticstoolbox import DHRobot, RevoluteDH # from spatialmath import SE3 @@ -40,7 +40,6 @@ class Jaco(DHRobot): """ def __init__(self, symbolic=False): - if symbolic: import spatialmath.base.symbolic as sym @@ -98,6 +97,5 @@ def __init__(self, symbolic=False): if __name__ == "__main__": # pragma nocover - jaco = Jaco(symbolic=False) print(jaco) diff --git a/roboticstoolbox/models/DH/KR5.py b/roboticstoolbox/models/DH/KR5.py index d29b026d5..210eb4c8c 100644 --- a/roboticstoolbox/models/DH/KR5.py +++ b/roboticstoolbox/models/DH/KR5.py @@ -48,10 +48,16 @@ def __init__(self): # Updated values form ARTE git. Old values left as comments L1 = RevoluteDH( - a=0.18, d=0.4, alpha=-pi / 2, qlim=[-155 * deg, 155 * deg] # alpha=pi / 2, + a=0.18, + d=0.4, + alpha=-pi / 2, + qlim=[-155 * deg, 155 * deg], # alpha=pi / 2, ) L2 = RevoluteDH( - a=0.6, d=0, alpha=0, qlim=[-180 * deg, 65 * deg] # d=0.135, # alpha=pi, + a=0.6, + d=0, + alpha=0, + qlim=[-180 * deg, 65 * deg], # d=0.135, # alpha=pi, ) L3 = RevoluteDH( a=0.12, @@ -66,7 +72,10 @@ def __init__(self): qlim=[-350 * deg, 350 * deg], ) L5 = RevoluteDH( - a=0.0, d=0.0, alpha=pi / 2, qlim=[-130 * deg, 130 * deg] # alpha=-pi / 2, + a=0.0, + d=0.0, + alpha=pi / 2, + qlim=[-130 * deg, 130 * deg], # alpha=-pi / 2, ) L6 = RevoluteDH(a=0, d=-0.115, alpha=pi, qlim=[-350 * deg, 350 * deg]) diff --git a/roboticstoolbox/models/DH/LWR4.py b/roboticstoolbox/models/DH/LWR4.py index 3eae730aa..1dac380a5 100755 --- a/roboticstoolbox/models/DH/LWR4.py +++ b/roboticstoolbox/models/DH/LWR4.py @@ -36,7 +36,6 @@ class LWR4(DHRobot): """ # noqa def __init__(self): - # deg = np.pi/180 mm = 1e-3 tool_offset = (103) * mm @@ -81,6 +80,5 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - robot = LWR4() print(robot) diff --git a/roboticstoolbox/models/DH/Mico.py b/roboticstoolbox/models/DH/Mico.py index 4becbcba4..a40d7b8fe 100644 --- a/roboticstoolbox/models/DH/Mico.py +++ b/roboticstoolbox/models/DH/Mico.py @@ -1,9 +1,8 @@ #!/usr/bin/env python -from math import pi, sin, cos +from math import sin, cos import numpy as np from roboticstoolbox import DHRobot, RevoluteDH -from spatialmath import SE3 class Mico(DHRobot): @@ -40,7 +39,6 @@ class Mico(DHRobot): """ def __init__(self, symbolic=False): - if symbolic: import spatialmath.base.symbolic as sym @@ -98,6 +96,5 @@ def __init__(self, symbolic=False): if __name__ == "__main__": # pragma nocover - mico = Mico(symbolic=False) print(mico) diff --git a/roboticstoolbox/models/DH/Orion5.py b/roboticstoolbox/models/DH/Orion5.py index b6127af3e..134f58aeb 100755 --- a/roboticstoolbox/models/DH/Orion5.py +++ b/roboticstoolbox/models/DH/Orion5.py @@ -42,7 +42,6 @@ class Orion5(DHRobot): """ def __init__(self, base=None): - mm = 1e-3 deg = pi / 180 @@ -87,6 +86,5 @@ def __init__(self, base=None): if __name__ == "__main__": # pragma nocover - orion = Orion5() print(orion) diff --git a/roboticstoolbox/models/DH/P8.py b/roboticstoolbox/models/DH/P8.py index cbed779cf..c550c12cb 100755 --- a/roboticstoolbox/models/DH/P8.py +++ b/roboticstoolbox/models/DH/P8.py @@ -19,7 +19,6 @@ import numpy as np from roboticstoolbox import DHRobot, PrismaticDH, models from spatialmath import SE3 -from spatialmath import base from math import pi @@ -51,7 +50,6 @@ class P8(DHRobot): """ # noqa def __init__(self): - # create the base links = [ PrismaticDH(alpha=-pi / 2, qlim=[-1, 1]), @@ -74,7 +72,6 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - robot = P8() print(robot) diff --git a/roboticstoolbox/models/DH/Panda.py b/roboticstoolbox/models/DH/Panda.py index 02774ec7e..4ef14dc79 100755 --- a/roboticstoolbox/models/DH/Panda.py +++ b/roboticstoolbox/models/DH/Panda.py @@ -31,7 +31,6 @@ class Panda(DHRobot): """ def __init__(self): - # deg = np.pi/180 mm = 1e-3 tool_offset = (103) * mm @@ -174,6 +173,5 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - panda = Panda() print(panda) diff --git a/roboticstoolbox/models/DH/Planar2.py b/roboticstoolbox/models/DH/Planar2.py index 4cf7d2254..da532886b 100644 --- a/roboticstoolbox/models/DH/Planar2.py +++ b/roboticstoolbox/models/DH/Planar2.py @@ -36,7 +36,6 @@ class Planar2(DHRobot): """ def __init__(self, symbolic=False): - if symbolic: import spatialmath.base.symbolic as sym @@ -65,6 +64,5 @@ def __init__(self, symbolic=False): if __name__ == "__main__": # pragma nocover - robot = Planar2() print(robot) diff --git a/roboticstoolbox/models/DH/Planar3.py b/roboticstoolbox/models/DH/Planar3.py index 8577cd011..133f61150 100644 --- a/roboticstoolbox/models/DH/Planar3.py +++ b/roboticstoolbox/models/DH/Planar3.py @@ -34,7 +34,6 @@ class Planar3(DHRobot): """ def __init__(self): - L = [RevoluteDH(a=1), RevoluteDH(a=1), RevoluteDH(a=1)] super().__init__(L, name="Planar 3 link", keywords=("planar",)) @@ -47,6 +46,5 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - robot = Planar3() print(robot) diff --git a/roboticstoolbox/models/DH/Puma560.py b/roboticstoolbox/models/DH/Puma560.py index ac0ffa44f..d65b4b4ef 100755 --- a/roboticstoolbox/models/DH/Puma560.py +++ b/roboticstoolbox/models/DH/Puma560.py @@ -18,7 +18,6 @@ # from math import pi import numpy as np from roboticstoolbox import DHRobot, RevoluteDH -from spatialmath import SE3 from spatialmath import base @@ -73,7 +72,6 @@ class Puma560(DHRobot): """ # noqa def __init__(self, symbolic=False): - if symbolic: import spatialmath.base.symbolic as sym @@ -239,7 +237,6 @@ def ikine_a(self, T, config="lun"): """ def ik3(robot, T, config="lun"): - config = self.config_validate(config, ("lr", "ud", "nf")) # solve for the first three joints @@ -293,8 +290,7 @@ def ik3(robot, T, config="lun"): with np.errstate(invalid="raise"): try: Psi = np.arccos( - (a2**2 - d4**2 - a3**2 + V114**2 + Pz**2) - / (2.0 * a2 * r) + (a2**2 - d4**2 - a3**2 + V114**2 + Pz**2) / (2.0 * a2 * r) ) except FloatingPointError: return "Out of reach" @@ -315,7 +311,6 @@ def ik3(robot, T, config="lun"): if __name__ == "__main__": # pragma nocover - puma = Puma560(symbolic=False) print(puma) print(puma.dynamics()) diff --git a/roboticstoolbox/models/DH/Sawyer.py b/roboticstoolbox/models/DH/Sawyer.py index 449ea6f7c..70003f638 100644 --- a/roboticstoolbox/models/DH/Sawyer.py +++ b/roboticstoolbox/models/DH/Sawyer.py @@ -37,7 +37,6 @@ class Sawyer(DHRobot): """ def __init__(self, symbolic=False): - if symbolic: import spatialmath.base.symbolic as sym @@ -81,6 +80,5 @@ def __init__(self, symbolic=False): if __name__ == "__main__": # pragma nocover - sawyer = Sawyer(symbolic=False) print(sawyer) diff --git a/roboticstoolbox/models/DH/Stanford.py b/roboticstoolbox/models/DH/Stanford.py index 102cc43ed..597a6fd9a 100755 --- a/roboticstoolbox/models/DH/Stanford.py +++ b/roboticstoolbox/models/DH/Stanford.py @@ -45,7 +45,6 @@ class Stanford(DHRobot): """ def __init__(self): - deg = pi / 180 inch = 0.0254 @@ -143,6 +142,5 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - stanford = Stanford() print(stanford) diff --git a/roboticstoolbox/models/DH/TwoLink.py b/roboticstoolbox/models/DH/TwoLink.py index 89b6d961c..9c38cb430 100644 --- a/roboticstoolbox/models/DH/TwoLink.py +++ b/roboticstoolbox/models/DH/TwoLink.py @@ -3,6 +3,7 @@ """ from roboticstoolbox import DHRobot, RevoluteDH + # from math import pi from spatialmath import SE3 import numpy as np @@ -57,7 +58,6 @@ class TwoLink(DHRobot): """ def __init__(self, symbolic=False): - if symbolic: import spatialmath.base.symbolic as sym @@ -104,6 +104,5 @@ def __init__(self, symbolic=False): if __name__ == "__main__": # pragma nocover - robot = TwoLink(symbolic=True) print(robot) diff --git a/roboticstoolbox/models/DH/UR10.py b/roboticstoolbox/models/DH/UR10.py index 0e38b9d35..9f2f8dc9b 100644 --- a/roboticstoolbox/models/DH/UR10.py +++ b/roboticstoolbox/models/DH/UR10.py @@ -1,6 +1,5 @@ import numpy as np from roboticstoolbox import DHRobot, RevoluteDH -from spatialmath import SE3 class UR10(DHRobot): @@ -39,7 +38,6 @@ class UR10(DHRobot): """ # noqa def __init__(self, symbolic=False): - if symbolic: import spatialmath.base.symbolic as sym @@ -93,7 +91,6 @@ def __init__(self, symbolic=False): if __name__ == "__main__": # pragma nocover - ur10 = UR10(symbolic=False) print(ur10) # print(ur10.dyntable()) diff --git a/roboticstoolbox/models/DH/UR3.py b/roboticstoolbox/models/DH/UR3.py index 88376d682..0108eedd9 100644 --- a/roboticstoolbox/models/DH/UR3.py +++ b/roboticstoolbox/models/DH/UR3.py @@ -1,6 +1,5 @@ import numpy as np from roboticstoolbox import DHRobot, RevoluteDH -from spatialmath import SE3 class UR3(DHRobot): @@ -39,7 +38,6 @@ class UR3(DHRobot): """ # noqa def __init__(self, symbolic=False): - if symbolic: import spatialmath.base.symbolic as sym @@ -93,7 +91,6 @@ def __init__(self, symbolic=False): if __name__ == "__main__": # pragma nocover - ur3 = UR3(symbolic=False) print(ur3) # print(ur3.dyntable()) diff --git a/roboticstoolbox/models/DH/UR5.py b/roboticstoolbox/models/DH/UR5.py index b248e6c78..79e023dba 100644 --- a/roboticstoolbox/models/DH/UR5.py +++ b/roboticstoolbox/models/DH/UR5.py @@ -1,6 +1,5 @@ import numpy as np from roboticstoolbox import DHRobot, RevoluteDH -from spatialmath import SE3 class UR5(DHRobot): @@ -39,7 +38,6 @@ class UR5(DHRobot): """ # noqa def __init__(self, symbolic=False): - if symbolic: import spatialmath.base.symbolic as sym @@ -93,7 +91,6 @@ def __init__(self, symbolic=False): if __name__ == "__main__": # pragma nocover - ur5 = UR5(symbolic=False) print(ur5) # print(ur5.dyntable()) diff --git a/roboticstoolbox/models/DH/__init__.py b/roboticstoolbox/models/DH/__init__.py index 0c7510fef..33aa7c269 100644 --- a/roboticstoolbox/models/DH/__init__.py +++ b/roboticstoolbox/models/DH/__init__.py @@ -25,28 +25,28 @@ __all__ = [ - 'Panda', - 'Puma560', - 'Stanford', - 'Ball', - 'Coil', - 'Hyper', - 'Hyper3d', - 'Cobra600', - 'IRB140', - 'KR5', - 'Orion5', - 'Planar3', - 'Planar2', - 'LWR4', - 'UR3', - 'UR5', - 'UR10', - 'Sawyer', - 'Mico', - 'Jaco', - 'Baxter', - 'TwoLink', - 'P8', - 'AL5D', + "Panda", + "Puma560", + "Stanford", + "Ball", + "Coil", + "Hyper", + "Hyper3d", + "Cobra600", + "IRB140", + "KR5", + "Orion5", + "Planar3", + "Planar2", + "LWR4", + "UR3", + "UR5", + "UR10", + "Sawyer", + "Mico", + "Jaco", + "Baxter", + "TwoLink", + "P8", + "AL5D", ] diff --git a/roboticstoolbox/models/ETS/Frankie.py b/roboticstoolbox/models/ETS/Frankie.py index 3b7de44c7..e55c1f39e 100644 --- a/roboticstoolbox/models/ETS/Frankie.py +++ b/roboticstoolbox/models/ETS/Frankie.py @@ -36,7 +36,6 @@ class Frankie(Robot): """ def __init__(self): - deg = np.pi / 180 mm = 1e-3 tool_offset = (103) * mm @@ -86,6 +85,5 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - robot = Frankie() print(robot) diff --git a/roboticstoolbox/models/ETS/GenericSeven.py b/roboticstoolbox/models/ETS/GenericSeven.py index 91bb31276..66c585c89 100644 --- a/roboticstoolbox/models/ETS/GenericSeven.py +++ b/roboticstoolbox/models/ETS/GenericSeven.py @@ -17,7 +17,6 @@ class GenericSeven(Robot): """ def __init__(self): - deg = np.pi / 180 mm = 1e-3 tool_offset = (103) * mm diff --git a/roboticstoolbox/models/ETS/Omni.py b/roboticstoolbox/models/ETS/Omni.py index f8836f42c..719042d97 100644 --- a/roboticstoolbox/models/ETS/Omni.py +++ b/roboticstoolbox/models/ETS/Omni.py @@ -37,7 +37,6 @@ class Omni(Robot): """ def __init__(self): - l, w, h = 0.55, 0.40, 0.35 b0 = Link(ETS(ET.Rz()), name="base0", parent=None, qlim=[-1000, 1000]) @@ -70,6 +69,5 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - robot = Omni() print(robot) diff --git a/roboticstoolbox/models/ETS/Panda.py b/roboticstoolbox/models/ETS/Panda.py index 592b94e3a..3deedb6e2 100644 --- a/roboticstoolbox/models/ETS/Panda.py +++ b/roboticstoolbox/models/ETS/Panda.py @@ -24,7 +24,6 @@ class Panda(Robot): """ def __init__(self): - deg = np.pi / 180 mm = 1e-3 tool_offset = (103) * mm @@ -65,6 +64,5 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - robot = Panda() print(robot) diff --git a/roboticstoolbox/models/ETS/Planar2.py b/roboticstoolbox/models/ETS/Planar2.py index 1603e9c13..06a90e297 100644 --- a/roboticstoolbox/models/ETS/Planar2.py +++ b/roboticstoolbox/models/ETS/Planar2.py @@ -25,7 +25,6 @@ class Planar2(Robot2): """ def __init__(self): - a1 = 1 a2 = 1 @@ -35,7 +34,7 @@ def __init__(self): super().__init__([l0, l1, l2], name="Planar2", comment="Planar 2D manipulator") - self.qb = np.array([0, np.pi/2]) + self.qb = np.array([0, np.pi / 2]) self.qz = np.zeros(2) self.addconfiguration("qz", self.qz) @@ -43,7 +42,6 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - robot = Planar2() print(robot) diff --git a/roboticstoolbox/models/ETS/Planar_Y.py b/roboticstoolbox/models/ETS/Planar_Y.py index 92d5f9f9d..0b2eb609c 100644 --- a/roboticstoolbox/models/ETS/Planar_Y.py +++ b/roboticstoolbox/models/ETS/Planar_Y.py @@ -25,7 +25,6 @@ class Planar_Y(Robot): """ def __init__(self): - # deg = np.pi / 180 # mm = 1e-3 tool_offset = 1 @@ -61,6 +60,5 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - robot = Planar_Y() print(robot) diff --git a/roboticstoolbox/models/ETS/Puma560.py b/roboticstoolbox/models/ETS/Puma560.py index 152f3f34d..ddb3ca462 100644 --- a/roboticstoolbox/models/ETS/Puma560.py +++ b/roboticstoolbox/models/ETS/Puma560.py @@ -65,6 +65,5 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - robot = Puma560() print(robot) diff --git a/roboticstoolbox/models/ETS/XYPanda.py b/roboticstoolbox/models/ETS/XYPanda.py index 8181cc53f..ef4ce8443 100644 --- a/roboticstoolbox/models/ETS/XYPanda.py +++ b/roboticstoolbox/models/ETS/XYPanda.py @@ -80,6 +80,5 @@ def __init__(self, workspace=5): if __name__ == "__main__": # pragma nocover - robot = XYPanda() print(robot) diff --git a/roboticstoolbox/models/URDF/AL5D.py b/roboticstoolbox/models/URDF/AL5D.py index 614aa5696..9ca8d1795 100644 --- a/roboticstoolbox/models/URDF/AL5D.py +++ b/roboticstoolbox/models/URDF/AL5D.py @@ -2,7 +2,6 @@ import numpy as np from roboticstoolbox.robot.Robot import Robot -from math import pi class AL5D(Robot): @@ -28,7 +27,6 @@ class AL5D(Robot): """ def __init__(self): - links, name, urdf_string, urdf_filepath = self.URDF_read( "al5d_description/urdf/al5d_robot.urdf" ) @@ -50,6 +48,5 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - robot = AL5D() print(robot) diff --git a/roboticstoolbox/models/URDF/Fetch.py b/roboticstoolbox/models/URDF/Fetch.py index cd5066ff2..428671358 100644 --- a/roboticstoolbox/models/URDF/Fetch.py +++ b/roboticstoolbox/models/URDF/Fetch.py @@ -30,7 +30,6 @@ class Fetch(Robot): """ def __init__(self): - links, name, urdf_string, urdf_filepath = self.URDF_read( "fetch_description/robots/fetch.urdf" ) @@ -54,7 +53,6 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - robot = Fetch() print(robot) diff --git a/roboticstoolbox/models/URDF/FetchCamera.py b/roboticstoolbox/models/URDF/FetchCamera.py index c98f9fcae..73bde3b9d 100644 --- a/roboticstoolbox/models/URDF/FetchCamera.py +++ b/roboticstoolbox/models/URDF/FetchCamera.py @@ -30,7 +30,6 @@ class FetchCamera(Robot): """ def __init__(self): - links, name, urdf_string, urdf_filepath = self.URDF_read( "fetch_description/robots/fetch_camera.urdf" ) @@ -55,7 +54,6 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - robot = FetchCamera() print(robot) diff --git a/roboticstoolbox/models/URDF/Frankie.py b/roboticstoolbox/models/URDF/Frankie.py index 34961a24d..b294ed047 100644 --- a/roboticstoolbox/models/URDF/Frankie.py +++ b/roboticstoolbox/models/URDF/Frankie.py @@ -31,7 +31,6 @@ class Frankie(Robot): """ def __init__(self): - links, name, urdf_string, urdf_filepath = self.URDF_read( "franka_description/robots/frankie_arm_hand.urdf.xacro" ) @@ -59,7 +58,6 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - robot = Frankie() print(robot) diff --git a/roboticstoolbox/models/URDF/FrankieOmni.py b/roboticstoolbox/models/URDF/FrankieOmni.py index 12101d4ec..014216c07 100644 --- a/roboticstoolbox/models/URDF/FrankieOmni.py +++ b/roboticstoolbox/models/URDF/FrankieOmni.py @@ -34,7 +34,6 @@ class FrankieOmni(Robot): """ def __init__(self): - links_base, _, urdf_string, urdf_filepath = self.URDF_read( "ridgeback_description/urdf/ridgeback.urdf.xacro" ) diff --git a/roboticstoolbox/models/URDF/KinovaGen3.py b/roboticstoolbox/models/URDF/KinovaGen3.py index 5e6a79fda..9db059bfa 100644 --- a/roboticstoolbox/models/URDF/KinovaGen3.py +++ b/roboticstoolbox/models/URDF/KinovaGen3.py @@ -30,7 +30,6 @@ class KinovaGen3(Robot): """ def __init__(self): - links, name, urdf_string, urdf_filepath = self.URDF_read( "kortex_description/robots/gen3.xacro" ) @@ -55,6 +54,5 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - robot = KinovaGen3() print(robot) diff --git a/roboticstoolbox/models/URDF/LBR.py b/roboticstoolbox/models/URDF/LBR.py index c881a66ba..90a2803d5 100644 --- a/roboticstoolbox/models/URDF/LBR.py +++ b/roboticstoolbox/models/URDF/LBR.py @@ -30,7 +30,6 @@ class LBR(Robot): """ def __init__(self): - links, name, urdf_string, urdf_filepath = self.URDF_read( "kuka_description/kuka_lbr_iiwa/urdf/lbr_iiwa_14_r820.xacro" ) @@ -55,6 +54,5 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - robot = LBR() print(robot) diff --git a/roboticstoolbox/models/URDF/Mico.py b/roboticstoolbox/models/URDF/Mico.py index f8fd2a46b..4be48bbba 100644 --- a/roboticstoolbox/models/URDF/Mico.py +++ b/roboticstoolbox/models/URDF/Mico.py @@ -29,7 +29,6 @@ class Mico(Robot): """ def __init__(self): - links, name, urdf_string, urdf_filepath = self.URDF_read( "kinova_description/urdf/j2n4s300_standalone.xacro" ) @@ -64,6 +63,5 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - robot = Mico() print(robot) diff --git a/roboticstoolbox/models/URDF/PR2.py b/roboticstoolbox/models/URDF/PR2.py index e1ab35b42..dc5a5547e 100644 --- a/roboticstoolbox/models/URDF/PR2.py +++ b/roboticstoolbox/models/URDF/PR2.py @@ -6,7 +6,6 @@ class PR2(Robot): def __init__(self): - links, name, urdf_string, urdf_filepath = self.URDF_read( "pr2_description/robots/pr2.urdf.xacro", xacro_tld="pr2_description" ) @@ -34,7 +33,6 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - r = PR2() # i = 0 diff --git a/roboticstoolbox/models/URDF/Panda.py b/roboticstoolbox/models/URDF/Panda.py index 9eb010300..528bb037f 100644 --- a/roboticstoolbox/models/URDF/Panda.py +++ b/roboticstoolbox/models/URDF/Panda.py @@ -31,7 +31,6 @@ class Panda(Robot): """ def __init__(self): - links, name, urdf_string, urdf_filepath = self.URDF_read( "franka_description/robots/panda_arm_hand.urdf.xacro" ) @@ -59,7 +58,6 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - r = Panda() r.qz diff --git a/roboticstoolbox/models/URDF/Puma560.py b/roboticstoolbox/models/URDF/Puma560.py index 7e946ee03..1643bc4ca 100644 --- a/roboticstoolbox/models/URDF/Puma560.py +++ b/roboticstoolbox/models/URDF/Puma560.py @@ -38,7 +38,6 @@ class Puma560(Robot): """ def __init__(self): - links, name, urdf_string, urdf_filepath = self.URDF_read( "puma560_description/urdf/puma560_robot.urdf.xacro" ) @@ -91,7 +90,6 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - robot = Puma560() print(robot) diff --git a/roboticstoolbox/models/URDF/UR10.py b/roboticstoolbox/models/URDF/UR10.py index 180b343c1..857f8c8a1 100644 --- a/roboticstoolbox/models/URDF/UR10.py +++ b/roboticstoolbox/models/URDF/UR10.py @@ -28,7 +28,6 @@ class UR10(Robot): """ def __init__(self): - links, name, urdf_string, urdf_filepath = self.URDF_read( "ur_description/urdf/ur10_joint_limited_robot.urdf.xacro" ) @@ -49,6 +48,5 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - robot = UR10() print(robot) diff --git a/roboticstoolbox/models/URDF/UR3.py b/roboticstoolbox/models/URDF/UR3.py index c59da8d16..c006321f4 100644 --- a/roboticstoolbox/models/URDF/UR3.py +++ b/roboticstoolbox/models/URDF/UR3.py @@ -28,7 +28,6 @@ class UR3(Robot): """ def __init__(self): - links, name, urdf_string, urdf_filepath = self.URDF_read( "ur_description/urdf/ur3_joint_limited_robot.urdf.xacro" ) @@ -49,6 +48,5 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - robot = UR3() print(robot) diff --git a/roboticstoolbox/models/URDF/UR5.py b/roboticstoolbox/models/URDF/UR5.py index 6bbfab9bc..e40e895eb 100644 --- a/roboticstoolbox/models/URDF/UR5.py +++ b/roboticstoolbox/models/URDF/UR5.py @@ -29,7 +29,6 @@ class UR5(Robot): """ def __init__(self): - links, name, urdf_string, urdf_filepath = self.URDF_read( "ur_description/urdf/ur5_joint_limited_robot.urdf.xacro" ) @@ -69,7 +68,6 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - robot = UR5() print(robot) print(robot.ets()) diff --git a/roboticstoolbox/models/URDF/Valkyrie.py b/roboticstoolbox/models/URDF/Valkyrie.py index 51b33b078..0147ce2a2 100644 --- a/roboticstoolbox/models/URDF/Valkyrie.py +++ b/roboticstoolbox/models/URDF/Valkyrie.py @@ -1,7 +1,7 @@ #!/usr/bin/env python import numpy as np -from roboticstoolbox.robot.Robot import Robot, Link +from roboticstoolbox.robot.Robot import Robot class Valkyrie(Robot): @@ -30,8 +30,7 @@ class Valkyrie(Robot): """ def __init__(self, variant="A"): - - if not variant in "ABCD": + if variant not in "ABCD": raise ValueError("variant must be in the range A-D") links, name, urdf_string, urdf_filepath = self.URDF_read( @@ -78,7 +77,6 @@ def __init__(self, variant="A"): if __name__ == "__main__": # pragma nocover - robot = Valkyrie("B") print(robot) env = robot.plot(np.zeros((robot.n,))) diff --git a/roboticstoolbox/models/URDF/YuMi.py b/roboticstoolbox/models/URDF/YuMi.py index fa0414586..ae55e2ee2 100644 --- a/roboticstoolbox/models/URDF/YuMi.py +++ b/roboticstoolbox/models/URDF/YuMi.py @@ -33,7 +33,6 @@ class YuMi(Robot): """ def __init__(self): - links, name, urdf_string, urdf_filepath = self.URDF_read( "yumi_description/urdf/yumi.urdf" ) @@ -105,6 +104,5 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - robot = YuMi() print(robot) diff --git a/roboticstoolbox/models/URDF/px100.py b/roboticstoolbox/models/URDF/px100.py index 243d02162..5caf215fb 100644 --- a/roboticstoolbox/models/URDF/px100.py +++ b/roboticstoolbox/models/URDF/px100.py @@ -31,7 +31,6 @@ class px100(Robot): """ def __init__(self): - links, name, urdf_string, urdf_filepath = self.URDF_read( "interbotix_descriptions/urdf/px100.urdf.xacro" ) @@ -52,6 +51,5 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - robot = px100() print(robot) diff --git a/roboticstoolbox/models/URDF/px150.py b/roboticstoolbox/models/URDF/px150.py index 30299d93e..ccbea7012 100644 --- a/roboticstoolbox/models/URDF/px150.py +++ b/roboticstoolbox/models/URDF/px150.py @@ -31,7 +31,6 @@ class px150(Robot): """ def __init__(self): - links, name, urdf_string, urdf_filepath = self.URDF_read( "interbotix_descriptions/urdf/px150.urdf.xacro" ) @@ -52,6 +51,5 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - robot = px150() print(robot) diff --git a/roboticstoolbox/models/URDF/rx150.py b/roboticstoolbox/models/URDF/rx150.py index 3a40bff18..36d1f53bd 100644 --- a/roboticstoolbox/models/URDF/rx150.py +++ b/roboticstoolbox/models/URDF/rx150.py @@ -31,7 +31,6 @@ class rx150(Robot): """ def __init__(self): - links, name, urdf_string, urdf_filepath = self.URDF_read( "interbotix_descriptions/urdf/rx150.urdf.xacro" ) @@ -52,6 +51,5 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - robot = rx150() print(robot) diff --git a/roboticstoolbox/models/URDF/rx200.py b/roboticstoolbox/models/URDF/rx200.py index 1a38d6591..50f5998bd 100644 --- a/roboticstoolbox/models/URDF/rx200.py +++ b/roboticstoolbox/models/URDF/rx200.py @@ -31,7 +31,6 @@ class rx200(Robot): """ def __init__(self): - links, name, urdf_string, urdf_filepath = self.URDF_read( "interbotix_descriptions/urdf/rx200.urdf.xacro" ) @@ -52,6 +51,5 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - robot = rx200() print(robot) diff --git a/roboticstoolbox/models/URDF/vx300.py b/roboticstoolbox/models/URDF/vx300.py index f195a6450..d79701263 100644 --- a/roboticstoolbox/models/URDF/vx300.py +++ b/roboticstoolbox/models/URDF/vx300.py @@ -31,7 +31,6 @@ class vx300(Robot): """ def __init__(self): - links, name, urdf_string, urdf_filepath = self.URDF_read( "interbotix_descriptions/urdf/vx300.urdf.xacro" ) @@ -52,6 +51,5 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - robot = vx300() print(robot) diff --git a/roboticstoolbox/models/URDF/vx300s.py b/roboticstoolbox/models/URDF/vx300s.py index 41f8db13e..9a61ee945 100644 --- a/roboticstoolbox/models/URDF/vx300s.py +++ b/roboticstoolbox/models/URDF/vx300s.py @@ -31,7 +31,6 @@ class vx300s(Robot): """ def __init__(self): - links, name, urdf_string, urdf_filepath = self.URDF_read( "interbotix_descriptions/urdf/vx300s.urdf.xacro" ) @@ -52,6 +51,5 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - robot = vx300s() print(robot) diff --git a/roboticstoolbox/models/URDF/wx200.py b/roboticstoolbox/models/URDF/wx200.py index 337328f37..5aa7c170f 100755 --- a/roboticstoolbox/models/URDF/wx200.py +++ b/roboticstoolbox/models/URDF/wx200.py @@ -31,7 +31,6 @@ class wx200(Robot): """ def __init__(self): - links, name, urdf_string, urdf_filepath = self.URDF_read( "interbotix_descriptions/urdf/wx200.urdf.xacro" ) @@ -52,6 +51,5 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - robot = wx200() print(robot) diff --git a/roboticstoolbox/models/URDF/wx250.py b/roboticstoolbox/models/URDF/wx250.py index 882273226..b5a8db517 100644 --- a/roboticstoolbox/models/URDF/wx250.py +++ b/roboticstoolbox/models/URDF/wx250.py @@ -31,7 +31,6 @@ class wx250(Robot): """ def __init__(self): - links, name, urdf_string, urdf_filepath = self.URDF_read( "interbotix_descriptions/urdf/wx250.urdf.xacro" ) @@ -52,6 +51,5 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - robot = wx250() print(robot) diff --git a/roboticstoolbox/models/URDF/wx250s.py b/roboticstoolbox/models/URDF/wx250s.py index 3a43e465c..3ffaf254e 100644 --- a/roboticstoolbox/models/URDF/wx250s.py +++ b/roboticstoolbox/models/URDF/wx250s.py @@ -31,7 +31,6 @@ class wx250s(Robot): """ def __init__(self): - links, name, urdf_string, urdf_filepath = self.URDF_read( "interbotix_descriptions/urdf/wx250s.urdf.xacro" ) @@ -52,6 +51,5 @@ def __init__(self): if __name__ == "__main__": # pragma nocover - robot = wx250s() print(robot) diff --git a/roboticstoolbox/models/list.py b/roboticstoolbox/models/list.py index 3eca2a98d..1dd71aaf0 100644 --- a/roboticstoolbox/models/list.py +++ b/roboticstoolbox/models/list.py @@ -1,4 +1,3 @@ -from typing import Type from roboticstoolbox.robot.Robot import Robot from roboticstoolbox.tools import rtb_get_param from roboticstoolbox.robot.ERobot import ERobot2 @@ -45,6 +44,7 @@ def list(keywords=None, dof=None, type=None, border="thin"): unicode = rtb_get_param("unicode") if not unicode: border = "ascii" + def make_table(border=None): table = ANSITable( Column("class", headalign="^", colalign="<"), @@ -112,7 +112,7 @@ def make_table(border=None): if __name__ == "__main__": # pragma nocover - list(border='ascii') - list(keywords=("dynamics",), border='thin') + list(border="ascii") + list(keywords=("dynamics",), border="thin") list(dof=6) list(keywords=("dynamics",), dof=6) diff --git a/roboticstoolbox/robot/DHFactor.py b/roboticstoolbox/robot/DHFactor.py index 6b838abc1..b517f54ed 100644 --- a/roboticstoolbox/robot/DHFactor.py +++ b/roboticstoolbox/robot/DHFactor.py @@ -1,4 +1,4 @@ -from roboticstoolbox import ETS, ET +from roboticstoolbox import ET import re import sympy import numpy as np @@ -7,11 +7,11 @@ pi2 = base.pi() / 2 deg = base.pi() / sympy.Integer("180") + # PROGRESS # subs2z does a bad thing in first phase, 2 subs it shouldnt make class DHFactor(ET): def __init__(self, axis=None, eta=None, **kwargs): - super().__init__(axis=axis, eta=eta, **kwargs) @classmethod @@ -63,7 +63,6 @@ def parse(cls, s): # ---------------------------------------------------------------------- # def simplify(self, mdh=False): - self.merge() self.swap(mdh) self.merge() @@ -104,7 +103,6 @@ def float_right(self): nchanges = 0 for i in range(len(self)): - this = self[i] if this.isjoint or this.isrevolute: continue @@ -130,7 +128,6 @@ def float_right(self): # ---------------------------------------------------------------------- # def swap(self, mdh=False): - # we want to sort terms into the order: # RZ # TX @@ -339,7 +336,6 @@ def add(this, that): # ---------------------------------------------------------------------- # def eliminate_y(self): - nchanges = 0 out = DHFactor() jointyet = False @@ -457,7 +453,6 @@ def __str__(self, q=None): # For each ET in the object, display it, data comes from properties # which come from the named tuple for et in self: - s = f"{et.axis}(" if et.isjoint: if q is not None: diff --git a/roboticstoolbox/robot/DHLink.py b/roboticstoolbox/robot/DHLink.py index a50d1e6a3..b09e2b298 100644 --- a/roboticstoolbox/robot/DHLink.py +++ b/roboticstoolbox/robot/DHLink.py @@ -10,10 +10,9 @@ from roboticstoolbox.robot.ETS import ETS from roboticstoolbox.robot.ET import ET from spatialmath import SE3 -from typing import List, Union +from typing import Union from functools import wraps from numpy import ndarray, cos, sin, array -from spatialgeometry import Shape from copy import deepcopy # _eps = np.finfo(np.float64).eps @@ -354,7 +353,6 @@ def __add__(self, L): raise TypeError("Cannot add a Link with a non Link object") def __str__(self): - s = "" if self.offset == 0: @@ -754,7 +752,6 @@ class RevoluteDH(DHLink): def __init__( self, d=0.0, a=0.0, alpha=0.0, offset=0.0, qlim=None, flip=False, **kwargs ): - theta = 0.0 sigma = 0 mdh = False @@ -819,7 +816,6 @@ class PrismaticDH(DHLink): def __init__( self, theta=0.0, a=0.0, alpha=0.0, offset=0.0, qlim=None, flip=False, **kwargs ): - d = 0.0 sigma = 1 mdh = False @@ -890,7 +886,6 @@ class RevoluteMDH(DHLink): def __init__( self, d=0.0, a=0.0, alpha=0.0, offset=0.0, qlim=None, flip=False, **kwargs ): - theta = 0.0 sigma = 0 mdh = True @@ -963,7 +958,6 @@ class PrismaticMDH(DHLink): def __init__( self, theta=0.0, a=0.0, alpha=0.0, offset=0.0, qlim=None, flip=False, **kwargs ): - d = 0.0 sigma = 1 mdh = True diff --git a/roboticstoolbox/robot/DHRobot.py b/roboticstoolbox/robot/DHRobot.py index 4fa75e7e3..511167f21 100644 --- a/roboticstoolbox/robot/DHRobot.py +++ b/roboticstoolbox/robot/DHRobot.py @@ -3,8 +3,6 @@ @author Jesse Haviland """ -from collections import namedtuple -from email import message from roboticstoolbox.tools.data import rtb_path_to_datafile import warnings import copy @@ -12,7 +10,6 @@ from roboticstoolbox.robot.Robot import Robot # DHLink from roboticstoolbox.robot.ETS import ETS, ET from roboticstoolbox.robot.DHLink import DHLink -from roboticstoolbox import rtb_set_param from spatialmath.base.argcheck import getvector, isscalar, verifymatrix, getmatrix # from spatialmath import base @@ -29,8 +26,7 @@ # from scipy.optimize import minimize, Bounds from ansitable import ANSITable, Column -from scipy.linalg import block_diag -from roboticstoolbox.robot.DHLink import _check_rne, DHLink +from roboticstoolbox.robot.DHLink import _check_rne from roboticstoolbox import rtb_get_param from roboticstoolbox.frne import init, frne, delete from numpy import any diff --git a/roboticstoolbox/robot/Dynamics.py b/roboticstoolbox/robot/Dynamics.py index 6a8c96802..0c607ece2 100644 --- a/roboticstoolbox/robot/Dynamics.py +++ b/roboticstoolbox/robot/Dynamics.py @@ -10,17 +10,16 @@ :todo: perhaps these should be abstract properties, methods of this calss """ + from collections import namedtuple from typing import Any, Callable, Dict, Union import numpy as np -from spatialmath.base import getvector, verifymatrix, isscalar, getmatrix, t2r, rot2jac +from spatialmath.base import getvector, verifymatrix, isscalar, getmatrix from scipy import integrate, interpolate -from spatialmath.base import symbolic as sym from roboticstoolbox import rtb_get_param from roboticstoolbox.robot.RobotProto import RobotProto from roboticstoolbox.tools.types import ArrayLike, NDArray -from typing_extensions import Self import roboticstoolbox as rtb from ansitable import ANSITable, Column @@ -28,7 +27,6 @@ class DynamicsMixin: - # --------------------------------------------------------------------- # def dynamics(self: RobotProto): """ @@ -339,7 +337,6 @@ def fdyn( _printProgressBar(0, prefix="Progress:", suffix="complete", length=60) while integrator.status == "running": - # step the integrator, calls _fdyn multiple times integrator.step() @@ -677,7 +674,6 @@ def jointdynamics(self: RobotProto, q, qd=None): tf = [] for j, link in enumerate(self.links): - # compute inertia for this joint zero = np.zeros((self.n)) qdd = np.zeros((self.n)) @@ -1154,7 +1150,6 @@ def coriolis_x( Ct = np.zeros((q.shape[0], 6, 6)) for k, (qk, qdk) in enumerate(zip(q, qd)): - if Ji is None: Ja = self.jacob0_analytical(q[0, :], representation) if pinv: @@ -1374,7 +1369,6 @@ def accel_x( xdd = np.zeros((q.shape[0], self.n)) for k, (qk, xdk, wk) in enumerate(zip(q, xd, w)): - Ja = self.jacob0_analytical(qk, representation=representation) if pinv: Ji = np.linalg.pinv(Ja) @@ -1619,7 +1613,6 @@ def perturb(self: RobotProto, p=0.1): def _printProgressBar( fraction, prefix="", suffix="", decimals=1, length=50, fill="█", printEnd="\r" ): - percent = ("{0:." + str(decimals) + "f}").format(fraction * 100) filledLength = int(length * fraction) bar = fill * filledLength + "-" * (length - filledLength) @@ -1627,7 +1620,6 @@ def _printProgressBar( if __name__ == "__main__": # pragma nocover - import roboticstoolbox as rtb puma = rtb.models.DH.Puma560() diff --git a/roboticstoolbox/robot/ERobot.py b/roboticstoolbox/robot/ERobot.py index 82803caa7..4a5ec00e4 100644 --- a/roboticstoolbox/robot/ERobot.py +++ b/roboticstoolbox/robot/ERobot.py @@ -8,7 +8,6 @@ class ERobot(Robot): def __init__(self, *args, **kwargs): - # warn("ERobot is deprecated, use iscollided instead", FutureWarning) super().__init__(*args, **kwargs) @@ -19,7 +18,6 @@ def __init__(self, *args, **kwargs): class ERobot2(Robot2): def __init__(self, *args, **kwargs): - # warn("ERobot2 is deprecated, use iscollided instead", FutureWarning) super().__init__(*args, **kwargs) diff --git a/roboticstoolbox/robot/ETS.py b/roboticstoolbox/robot/ETS.py index ee339d6d2..8a9bf346e 100644 --- a/roboticstoolbox/robot/ETS.py +++ b/roboticstoolbox/robot/ETS.py @@ -435,12 +435,10 @@ def m(self) -> int: return self._m @overload - def data(self: "ETS") -> List[ET]: - ... # pragma: nocover + def data(self: "ETS") -> List[ET]: ... # pragma: nocover @overload - def data(self: "ETS2") -> List[ET2]: - ... # pragma: nocover + def data(self: "ETS2") -> List[ET2]: ... # pragma: nocover @property def data(self): @@ -448,25 +446,21 @@ def data(self): @data.setter @overload - def data(self: "ETS", new_data: List[ET]): - ... # pragma: nocover + def data(self: "ETS", new_data: List[ET]): ... # pragma: nocover @data.setter @overload - def data(self: "ETS", new_data: List[ET2]): - ... # pragma: nocover + def data(self: "ETS", new_data: List[ET2]): ... # pragma: nocover @data.setter def data(self, new_data): self._data = new_data @overload - def pop(self: "ETS", i: int = -1) -> ET: - ... # pragma: nocover + def pop(self: "ETS", i: int = -1) -> ET: ... # pragma: nocover @overload - def pop(self: "ETS2", i: int = -1) -> ET2: - ... # pragma: nocover + def pop(self: "ETS2", i: int = -1) -> ET2: ... # pragma: nocover def pop(self, i=-1): """ @@ -506,12 +500,10 @@ def pop(self, i=-1): return item @overload - def split(self: "ETS") -> List["ETS"]: - ... # pragma: nocover + def split(self: "ETS") -> List["ETS"]: ... # pragma: nocover @overload - def split(self: "ETS2") -> List["ETS2"]: - ... # pragma: nocover + def split(self: "ETS2") -> List["ETS2"]: ... # pragma: nocover def split(self): """ @@ -548,12 +540,10 @@ def split(self): return segments @overload - def inv(self: "ETS") -> "ETS": - ... # pragma: nocover + def inv(self: "ETS") -> "ETS": ... # pragma: nocover @overload - def inv(self: "ETS2") -> "ETS2": - ... # pragma: nocover + def inv(self: "ETS2") -> "ETS2": ... # pragma: nocover def inv(self): r""" @@ -589,20 +579,16 @@ def inv(self): return self.__class__([et.inv() for et in reversed(self.data)]) @overload - def __getitem__(self: "ETS", i: int) -> ET: - ... # pragma: nocover + def __getitem__(self: "ETS", i: int) -> ET: ... # pragma: nocover @overload - def __getitem__(self: "ETS", i: slice) -> List[ET]: - ... # pragma: nocover + def __getitem__(self: "ETS", i: slice) -> List[ET]: ... # pragma: nocover @overload - def __getitem__(self: "ETS2", i: int) -> ET2: - ... # pragma: nocover + def __getitem__(self: "ETS2", i: int) -> ET2: ... # pragma: nocover @overload - def __getitem__(self: "ETS2", i: slice) -> List[ET2]: - ... # pragma: nocover + def __getitem__(self: "ETS2", i: slice) -> List[ET2]: ... # pragma: nocover def __getitem__(self, i): """ diff --git a/roboticstoolbox/robot/Gripper.py b/roboticstoolbox/robot/Gripper.py index 1d35d5ddb..807ba2863 100644 --- a/roboticstoolbox/robot/Gripper.py +++ b/roboticstoolbox/robot/Gripper.py @@ -25,7 +25,6 @@ def __init__( name: str = "", tool: Union[NDArray, SE3, None] = None, ): - self._n = 0 self.name = name @@ -50,7 +49,6 @@ def __init__( if link.isjoint ] ): - jindex = [0] # "mutable integer" hack def visit_link(link, jindex): diff --git a/roboticstoolbox/robot/Link.py b/roboticstoolbox/robot/Link.py index c8b6dc2b8..e58fb4e41 100644 --- a/roboticstoolbox/robot/Link.py +++ b/roboticstoolbox/robot/Link.py @@ -271,12 +271,10 @@ def Ts(self) -> Union[NDArray, None]: return self._Ts @overload - def ets(self: "Link") -> ETS: - ... # pragma: nocover + def ets(self: "Link") -> ETS: ... # pragma: nocover @overload - def ets(self: "Link2") -> ETS2: - ... # pragma: nocover + def ets(self: "Link2") -> ETS2: ... # pragma: nocover @property def ets(self) -> ETS: @@ -302,13 +300,11 @@ def ets(self) -> ETS: @ets.setter @overload - def ets(self: "Link", new_ets: ETS): - ... # pragma: nocover + def ets(self: "Link", new_ets: ETS): ... # pragma: nocover @ets.setter @overload - def ets(self: "Link2", new_ets: ETS2): - ... # pragma: nocover + def ets(self: "Link2", new_ets: ETS2): ... # pragma: nocover @ets.setter def ets(self, new_ets): @@ -395,12 +391,10 @@ def _repr_pretty_(self, p, cycle): # -------------------------------------------------------------------------- # @overload - def copy(self: "Link") -> "Link": - ... # pragma: nocover + def copy(self: "Link") -> "Link": ... # pragma: nocover @overload - def copy(self: "Link2") -> "Link2": - ... # pragma: nocover + def copy(self: "Link2") -> "Link2": ... # pragma: nocover def copy(self): """ @@ -466,12 +460,10 @@ def __deepcopy__(self, memo): # -------------------------------------------------------------------------- # @overload - def v(self: "Link") -> Union["ET", None]: - ... # pragma: nocover + def v(self: "Link") -> Union["ET", None]: ... # pragma: nocover @overload - def v(self: "Link2") -> Union["ET2", None]: - ... # pragma: nocover + def v(self: "Link2") -> Union["ET2", None]: ... # pragma: nocover @property def v(self): @@ -1130,7 +1122,11 @@ def nchildren(self) -> int: def closest_point( self, shape: Shape, inf_dist: float = 1.0, skip: bool = False - ) -> Tuple[Union[int, None], Union[NDArray, None], Union[NDArray, None],]: + ) -> Tuple[ + Union[int, None], + Union[NDArray, None], + Union[NDArray, None], + ]: """ Finds the closest point to a shape @@ -1367,9 +1363,7 @@ def format(l, fmt, val): # noqa return dyn - def _format_param( - self, l, name, symbol=None, ignorevalue=None, indices=None - ): # noqa # pragma nocover + def _format_param(self, l, name, symbol=None, ignorevalue=None, indices=None): # noqa # pragma nocover # if value == ignorevalue then don't display it v = getattr(self, name) @@ -1404,7 +1398,6 @@ def _format_param( l.append(s) def _params(self, name: bool = True): # pragma nocover - l = [] # noqa if name: self._format_param(l, "name") diff --git a/roboticstoolbox/robot/PoERobot.py b/roboticstoolbox/robot/PoERobot.py index 5c0edaf46..741a6e35d 100644 --- a/roboticstoolbox/robot/PoERobot.py +++ b/roboticstoolbox/robot/PoERobot.py @@ -106,11 +106,10 @@ def __repr__(self): s += "\n".join([" " + repr(link) + "," for link in self]) s += "\n ],\n" s += f" T0=SE3({np.array_repr(self.T0.A)}),\n" - s += f" name=\"{self.name}\",\n" + s += f' name="{self.name}",\n' s += ")" return s - def nbranches(self): return 0 @@ -182,7 +181,6 @@ def ets(self): if __name__ == "__main__": # pragma nocover - T0 = SE3.Trans(2, 0, 0) # rotate about z-axis, through (0,0,0) diff --git a/roboticstoolbox/robot/Robot.py b/roboticstoolbox/robot/Robot.py index eadab10d4..0e7fcc116 100644 --- a/roboticstoolbox/robot/Robot.py +++ b/roboticstoolbox/robot/Robot.py @@ -1188,7 +1188,11 @@ def jacobm( verifymatrix(H, (6, self.n, self.n)) manipulability = self.manipulability( - q, J=J, start=start, end=end, axes=axes # type: ignore + q, + J=J, + start=start, + end=end, + axes=axes, # type: ignore ) J = J[axes, :] # type: ignore @@ -1209,7 +1213,11 @@ def jacobm( def closest_point( self, q: ArrayLike, shape: Shape, inf_dist: float = 1.0, skip: bool = False - ) -> Tuple[Union[int, None], Union[NDArray, None], Union[NDArray, None],]: + ) -> Tuple[ + Union[int, None], + Union[NDArray, None], + Union[NDArray, None], + ]: """ Find the closest point between robot and shape @@ -1443,7 +1451,8 @@ def indiv_calculation(link: Link, link_col: CollisionShape, q: NDArray): norm = lpTcp / d norm_h = np.expand_dims( - np.concatenate((norm, [0.0, 0.0, 0.0])), axis=0 # type: ignore + np.concatenate((norm, [0.0, 0.0, 0.0])), + axis=0, # type: ignore ) # tool = (self.fkine(q, end=link).inv() * SE3(wTlp)).A[:3, 3] @@ -1576,7 +1585,8 @@ def rotation_between_vectors(a, b): # Create line of sight object los_mid = SE3((wTcp + wTtp) / 2) los_orientation = rotation_between_vectors( - np.array([0.0, 0.0, 1.0]), wTcp - wTtp # type: ignore + np.array([0.0, 0.0, 1.0]), + wTcp - wTtp, # type: ignore ) los = Cylinder( @@ -1592,9 +1602,7 @@ def indiv_calculation(link: Link, link_col: CollisionShape, q: NDArray): lpTvp = -wTlp + wTvp norm = lpTvp / d - norm_h = np.expand_dims( - np.concatenate((norm, [0.0, 0.0, 0.0])), axis=0 - ) # type: ignore + norm_h = np.expand_dims(np.concatenate((norm, [0.0, 0.0, 0.0])), axis=0) # type: ignore tool = SE3( (np.linalg.inv(self.fkine(q, end=link).A) @ SE3(wTlp).A)[:3, 3] @@ -1608,9 +1616,7 @@ def indiv_calculation(link: Link, link_col: CollisionShape, q: NDArray): Jv = camera.jacob0(camera.q) Jv[:3, :] = self._T[:3, :3] @ Jv[:3, :] - Jv *= ( - np.linalg.norm(wTvp - shape.T[:3, -1]) / los.length - ) # type: ignore + Jv *= np.linalg.norm(wTvp - shape.T[:3, -1]) / los.length # type: ignore dpc = norm_h @ Jv dpc = np.concatenate( diff --git a/roboticstoolbox/robot/RobotProto.py b/roboticstoolbox/robot/RobotProto.py index 0f9768c53..33455613f 100644 --- a/roboticstoolbox/robot/RobotProto.py +++ b/roboticstoolbox/robot/RobotProto.py @@ -1,56 +1,45 @@ -import numpy as np from roboticstoolbox.tools.types import ArrayLike, NDArray -from typing import Any, Callable, Generic, List, Set, TypeVar, Union, Dict, Tuple, Type +from typing import Any, Callable, List, Union, Dict from typing_extensions import Protocol, Self from roboticstoolbox.robot.Link import Link, BaseLink from roboticstoolbox.robot.Gripper import Gripper from roboticstoolbox.robot.ETS import ETS from spatialmath import SE3 -import roboticstoolbox as rtb class KinematicsProtocol(Protocol): @property - def _T(self) -> NDArray: - ... + def _T(self) -> NDArray: ... def ets( self, start: Union[Link, Gripper, str, None] = None, end: Union[Link, Gripper, str, None] = None, - ) -> ETS: - ... + ) -> ETS: ... class RobotProto(Protocol): @property - def links(self) -> List[BaseLink]: - ... + def links(self) -> List[BaseLink]: ... @property - def n(self) -> int: - ... + def n(self) -> int: ... @property - def q(self) -> NDArray: - ... + def q(self) -> NDArray: ... @property - def name(self) -> str: - ... + def name(self) -> str: ... @name.setter - def name(self, new_name: str): - ... + def name(self, new_name: str): ... @property - def gravity(self) -> NDArray: - ... + def gravity(self) -> NDArray: ... - def dynchanged(self): - ... + def dynchanged(self): ... def jacobe( self, @@ -58,8 +47,7 @@ def jacobe( end: Union[str, BaseLink, Gripper, None] = None, start: Union[str, BaseLink, Gripper, None] = None, tool: Union[NDArray, SE3, None] = None, - ) -> NDArray: - ... + ) -> NDArray: ... def jacob0( self, @@ -67,17 +55,13 @@ def jacob0( end: Union[str, BaseLink, Gripper, None] = None, start: Union[str, BaseLink, Gripper, None] = None, tool: Union[NDArray, SE3, None] = None, - ) -> NDArray: - ... + ) -> NDArray: ... - def copy(self) -> Self: - ... + def copy(self) -> Self: ... - def accel(self, q, qd, torque, gravity=None) -> NDArray: - ... + def accel(self, q, qd, torque, gravity=None) -> NDArray: ... - def nofriction(self, coulomb: bool, viscous: bool) -> Self: - ... + def nofriction(self, coulomb: bool, viscous: bool) -> Self: ... def _fdyn( self, @@ -85,8 +69,7 @@ def _fdyn( x: NDArray, torqfun: Callable[[Any], NDArray], targs: Dict, - ) -> NDArray: - ... + ) -> NDArray: ... def rne( self, @@ -95,13 +78,11 @@ def rne( qdd: NDArray, symbolic: bool = False, gravity: Union[None, ArrayLike] = None, - ) -> NDArray: - ... + ) -> NDArray: ... def gravload( self, q: Union[None, ArrayLike] = None, gravity: Union[None, ArrayLike] = None - ): - ... + ): ... def pay( self, @@ -109,5 +90,4 @@ def pay( q: Union[NDArray, None] = None, J: Union[NDArray, None] = None, frame: int = 1, - ): - ... + ): ... diff --git a/roboticstoolbox/tools/DHFactor.py b/roboticstoolbox/tools/DHFactor.py index 2842fd784..2302a7086 100644 --- a/roboticstoolbox/tools/DHFactor.py +++ b/roboticstoolbox/tools/DHFactor.py @@ -3,8 +3,7 @@ """ -class Element: # pragma nocover - +class Element: # pragma nocover TX = 0 TY = 1 TZ = 2 @@ -28,15 +27,10 @@ class Element: # pragma nocover dhModified = (RX, 0), (TX, 0), (RZ, 1), (TZ, 1) def __init__( - self, - elementIn=None, - stringIn=None, - eltype=None, - constant=None, - sign=0): - - self.var = None # eg. q1, for joint var types - self.symconst = None # eg. L1, for lengths + self, elementIn=None, stringIn=None, eltype=None, constant=None, sign=0 + ): + self.var = None # eg. q1, for joint var types + self.symconst = None # eg. L1, for lengths # DH parameters, only set if type is DH_STANDARD/MODIFIED self.theta = None @@ -48,11 +42,10 @@ def __init__( if stringIn: if elementIn or eltype or constant: - raise ValueError( - "if parsing a string, string must be the only input") + raise ValueError("if parsing a string, string must be the only input") i = None - sType = stringIn[0:2] # Tx, Rx etc - sRest = stringIn[2:] # the argument including brackets + sType = stringIn[0:2] # Tx, Rx etc + sRest = stringIn[2:] # the argument including brackets if not (sRest[-1] == ")" and sRest[0] == "("): raise ValueError("brackets") @@ -66,18 +59,18 @@ def __init__( if not match: raise ValueError("bad transform name: " + sType) - sRest = sRest[1:-1] # get the argument from between brackets + sRest = sRest[1:-1] # get the argument from between brackets # handle an optional minus sign negative = "" - if sRest[0] == '-': + if sRest[0] == "-": negative = "-" sRest = sRest[1] if sRest[0] == "q": self.var = negative + sRest - elif sRest[0] == 'L': + elif sRest[0] == "L": self.symconst = negative + sRest else: try: @@ -104,7 +97,7 @@ def __init__( if eltype: self.eltype = eltype if constant: - self.constant = constant # eg. 90, for angles + self.constant = constant # eg. 90, for angles if sign < 0: self.negate() @@ -115,12 +108,18 @@ def showRuleUsage(): print("Rule " + str(i) + ": " + str(Element.rules[i])) def istrans(self): - return (self.eltype == self.TX) or (self.eltype == self.TY) \ + return ( + (self.eltype == self.TX) + or (self.eltype == self.TY) or (self.eltype == self.TZ) + ) def isrot(self): - return (self.eltype == self.RX) or (self.eltype == self.RY) or \ - (self.eltype == self.RZ) + return ( + (self.eltype == self.RX) + or (self.eltype == self.RY) + or (self.eltype == self.RZ) + ) def isjoint(self): return self.var is not None @@ -136,7 +135,6 @@ def axis(self): raise ValueError("bad transform type") def symAdd(self, s1, s2): - if s1 is None and s2 is None: return None elif s1 and s2 is None: @@ -150,8 +148,7 @@ def symAdd(self, s1, s2): return s1 + "+" + s2 def add(self, e): - if self.eltype != Element.DH_STANDARD and \ - self.eltype != Element.DH_MODIFIED: + if self.eltype != Element.DH_STANDARD and self.eltype != Element.DH_MODIFIED: raise ValueError("wrong element type " + str(self)) print(" adding: " + str(self) + " += " + str(e)) if e.eltype == self.RZ: @@ -179,7 +176,6 @@ def add(self, e): # test if this particular element could be part of a DH term # eg. Rz(q1) can be, Rx(q1) cannot. def factorMatch(self, dhWhich, i, verbose): - dhFactors = None match = False @@ -191,12 +187,24 @@ def factorMatch(self, dhWhich, i, verbose): raise ValueError("bad DH type") match = (self.eltype == dhFactors[i][0]) and not ( - (dhFactors[i][1] == 0) and self.isjoint()) + (dhFactors[i][1] == 0) and self.isjoint() + ) if verbose > 0: - print(" matching " + str(self) + " (i=" + str(i) + ") " + - " to " + self.typeName[dhFactors[i][0]] + "<" + - str(dhFactors[i][1]) + ">" + " -> " + str(match)) + print( + " matching " + + str(self) + + " (i=" + + str(i) + + ") " + + " to " + + self.typeName[dhFactors[i][0]] + + "<" + + str(dhFactors[i][1]) + + ">" + + " -> " + + str(match) + ) return match def merge(self, e): @@ -230,32 +238,46 @@ def swap(self, next, dhWhich): if dhWhich == Element.DH_STANDARD: # order = [2, 0, 3, 4, 0, 1] - if self.eltype == Element.TZ and next.eltype == Element.TX or \ - self.eltype == Element.TX and next.eltype == Element.RX \ - and next.isjoint() or \ - self.eltype == Element.TY and next.eltype == Element.RY \ - and next.isjoint() or \ - self.eltype == Element.TZ and next.eltype == Element.RZ \ - and next.isjoint() or \ - not self.isjoint() and self.eltype == Element.RX and \ - next.eltype == Element.TX or \ - not self.isjoint() and self.eltype == Element.RY and \ - next.eltype == Element.TY or \ - not self.isjoint() and not next.isjoint() and \ - self.eltype == Element.TZ and \ - next.eltype == Element.RZ or \ - self.eltype == Element.TY and \ - next.eltype == Element.TZ or \ - self.eltype == Element.TY and next.eltype == Element.TX: + if ( + self.eltype == Element.TZ + and next.eltype == Element.TX + or self.eltype == Element.TX + and next.eltype == Element.RX + and next.isjoint() + or self.eltype == Element.TY + and next.eltype == Element.RY + and next.isjoint() + or self.eltype == Element.TZ + and next.eltype == Element.RZ + and next.isjoint() + or not self.isjoint() + and self.eltype == Element.RX + and next.eltype == Element.TX + or not self.isjoint() + and self.eltype == Element.RY + and next.eltype == Element.TY + or not self.isjoint() + and not next.isjoint() + and self.eltype == Element.TZ + and next.eltype == Element.RZ + or self.eltype == Element.TY + and next.eltype == Element.TZ + or self.eltype == Element.TY + and next.eltype == Element.TX + ): print("Swap: " + self + " <-> " + next) return True elif dhWhich == Element.DH_MODIFIED: - if self.eltype == Element.RX and next.eltype == Element.TX or \ - self.eltype == Element.RY and \ - next.eltype == Element.TY or \ - self.eltype == Element.RZ and \ - next.eltype == Element.TZ or \ - self.eltype == Element.TZ and next.eltype == Element.TX: + if ( + self.eltype == Element.RX + and next.eltype == Element.TX + or self.eltype == Element.RY + and next.eltype == Element.TY + or self.eltype == Element.RZ + and next.eltype == Element.TZ + or self.eltype == Element.TZ + and next.eltype == Element.TX + ): print("Swap: " + self + " <-> " + next) return True else: @@ -264,7 +286,6 @@ def swap(self, next, dhWhich): # negate the arguments of the element def negate(self): - self.constant = -self.constant if self.symconst: @@ -279,18 +300,25 @@ def negate(self): s[i] = "+" if s[0] == "+": s.pop(0) - s = "".join(s) # lgtm [py/unused-local-variable] + s = "".join(s) # lgtm [py/unused-local-variable] - ''' + """ Return a string representation of the parameters (argument) of the element, which can be a number, symbolic constant, or a joint variable. - ''' + """ + def argString(self): s = "" - if self.eltype == Element.RX or Element.RY or Element.RZ or \ - Element.TX or Element.TY or Element.TZ: + if ( + self.eltype == Element.RX + or Element.RY + or Element.RZ + or Element.TX + or Element.TY + or Element.TZ + ): if self.var: s = self.var if self.symconst: @@ -301,9 +329,9 @@ def argString(self): # constants always displayed with a sign character if self.constant != 0.0: if self.constant >= 0.0: - s = s + "+" + '{0:.3f}'.format(self.constant) + s = s + "+" + "{0:.3f}".format(self.constant) else: - s = s + '{0:.3f}'.format(self.constant) + s = s + "{0:.3f}".format(self.constant) elif self.eltype == Element.DH_STANDARD or Element.DH_MODIFIED: # theta, d, a, alpha # theta @@ -311,12 +339,12 @@ def argString(self): # revolute joint s = s + self.var if self.offset >= 0: - s = s + "+" + '{0:.3f}'.format(self.offset) + s = s + "+" + "{0:.3f}".format(self.offset) elif self.offset < 0: - s = s + '{0:.3f}'.format(self.offset) + s = s + "{0:.3f}".format(self.offset) else: # prismatic joint - s = s + '{0:.3f}'.format(self.theta) + s = s + "{0:.3f}".format(self.theta) s = s + ", " # d @@ -331,7 +359,7 @@ def argString(self): s = s + ", " # alpha - s = s + '{0:.3f}'.format(self.alpha) + s = s + "{0:.3f}".format(self.alpha) else: raise ValueError("bad Element type") return s diff --git a/roboticstoolbox/tools/Ticker.py b/roboticstoolbox/tools/Ticker.py index 4b8cd428a..1a95dc95a 100644 --- a/roboticstoolbox/tools/Ticker.py +++ b/roboticstoolbox/tools/Ticker.py @@ -3,20 +3,19 @@ class Ticker(threading.Thread): # pragma nocover - def __init__(self, period): super().__init__() self.sem = threading.Semaphore(0) self.done = False self.period = period - print('in init', period) + print("in init", period) def wait(self): self.sem.acquire() def run(self): - print('in run', self.period) + print("in run", self.period) start = time.time() stop = start while not self.done: @@ -49,6 +48,6 @@ def stop(self): if i > 0: tsum += terr tmax = max(tmax, terr) - print('.', end='', flush=True) + print(".", end="", flush=True) t.stop() print(f"\nmean = {tsum/(N-1):.2f}, max = {tmax:.2f}") diff --git a/roboticstoolbox/tools/data.py b/roboticstoolbox/tools/data.py index a7f25716a..c272a7cd5 100644 --- a/roboticstoolbox/tools/data.py +++ b/roboticstoolbox/tools/data.py @@ -1,5 +1,4 @@ from pathlib import Path -import sys import importlib @@ -13,22 +12,21 @@ def rtb_load_matfile(filename): :return: contents of mat data file :rtype: dict - Reads a MATLAB format *mat* file which can contain multiple variables, in + Reads a MATLAB format *mat* file which can contain multiple variables, in a binary or ASCII format. Returns a dict where the keys are the variable names and the values are NumPy arrays. .. note:: - Uses SciPy ``io.loadmat`` to do the work. - - If the filename has no path component, eg. ``map1.mat`, it will be + - If the filename has no path component, eg. ``map1.mat`, it will be first be looked for in the folder ``roboticstoolbox/data``. - MATLAB structs are converted to Python dicts, but extended so that elements can be accessed using dot notation. - + :seealso: :func:`path_to_datafile` """ from scipy.io import loadmat from scipy.io.matlab.mio5_params import mat_struct - from collections import namedtuple # get results as a dict data = rtb_load_data(filename, loadmat, squeeze_me=True, struct_as_record=False) @@ -37,7 +35,6 @@ def rtb_load_matfile(filename): # were a MATLAB struct, convert them to a namedtuple for key, value in data.items(): if isinstance(value, mat_struct): - # extend dict with a dot access method class dictx(dict): def __getattr__(self, key): @@ -50,9 +47,10 @@ def __getattr__(self, key): for v in value._fieldnames: d[v] = getattr(value, v) data[key] = d - + return data + def rtb_load_jsonfile(filename): """ Load toolbox JSON format data file @@ -68,14 +66,15 @@ def rtb_load_jsonfile(filename): names and the values are NumPy arrays. .. note:: - - If the filename has no path component, eg. ``map1.mat`, it will be + - If the filename has no path component, eg. ``map1.mat`, it will be first be looked for in the folder ``roboticstoolbox/data``. - + :seealso: :func:`path_to_datafile` """ import json - return rtb_load_data(filename, lambda f: json.load(open(f, 'r'))) + return rtb_load_data(filename, lambda f: json.load(open(f, "r"))) + def rtb_load_data(filename, handler, **kwargs): """ @@ -97,8 +96,8 @@ def rtb_load_data(filename, handler, **kwargs): data = rtb_load_data('data/queensland.json', lambda f: json.load(open(f, 'r'))) - - .. note:: If the filename has no path component, eg. ``foo.dat``, it will + + .. note:: If the filename has no path component, eg. ``foo.dat``, it will first be looked for in the folder ``roboticstoolbox/data``. :seealso: :func:`path_to_datafile` @@ -106,6 +105,7 @@ def rtb_load_data(filename, handler, **kwargs): path = rtb_path_to_datafile(filename, local=False) return handler(path, **kwargs) + def rtb_path_to_datafile(*filename, local=True): """ Get absolute path to datafile @@ -147,18 +147,17 @@ def rtb_path_to_datafile(*filename, local=True): rtbdata = importlib.import_module("rtbdata") root = Path(rtbdata.__path__[0]) - + path = root / filename if path.exists(): return path.resolve() else: raise ValueError(f"file {filename} not found locally or in rtbdata") -if __name__ == "__main__": - house = rtb_load_matfile("data/house.mat"); +if __name__ == "__main__": + house = rtb_load_matfile("data/house.mat") a = rtb_loadmat("map1.mat") print(a) a = rtb_loadmat("data/map1.mat") print(a) - diff --git a/roboticstoolbox/tools/jsingu.py b/roboticstoolbox/tools/jsingu.py index 2b565f6d7..7bfd8ddc7 100644 --- a/roboticstoolbox/tools/jsingu.py +++ b/roboticstoolbox/tools/jsingu.py @@ -1,5 +1,4 @@ import numpy as np -from scipy.linalg import lu # def jsingu(J): @@ -15,8 +14,8 @@ # ', '.join( # [str(i) for i in range(j) if all(U[:, j] == U[:, i])])) -def jsingu(J): +def jsingu(J): indep_columns = np.empty((J.shape[0], 0)) rank = 0 for j in range(J.shape[1]): @@ -37,10 +36,10 @@ def jsingu(J): elif i > 0: s += " + " s += f"{abs(ci):.3g} column_{i}" - print(s) + print(s) -if __name__ == "__main__": # pragma nocover +if __name__ == "__main__": # pragma nocover import roboticstoolbox as rtb # robot = rtb.models.DH.Puma560() diff --git a/roboticstoolbox/tools/null.py b/roboticstoolbox/tools/null.py index d084cf2dd..947ae8888 100644 --- a/roboticstoolbox/tools/null.py +++ b/roboticstoolbox/tools/null.py @@ -7,7 +7,7 @@ def null(A, atol=1e-13, rtol=0): - ''' + """ Compute an approximate basis for the nullspace of A. The algorithm used by this function is based on the singular value @@ -38,7 +38,7 @@ def null(A, atol=1e-13, rtol=0): zero. :rtype ns: ndarray - ''' + """ A = np.atleast_2d(A) u, s, vh = np.linalg.svd(A) diff --git a/roboticstoolbox/tools/numerical.py b/roboticstoolbox/tools/numerical.py index 8c8c7657b..454b3ec3f 100644 --- a/roboticstoolbox/tools/numerical.py +++ b/roboticstoolbox/tools/numerical.py @@ -1,6 +1,7 @@ import numpy as np from spatialmath import base + def jacobian_numerical(f, x, dx=1e-8, N=0): r""" Numerically compute Jacobian of function @@ -16,7 +17,7 @@ def jacobian_numerical(f, x, dx=1e-8, N=0): :return: Jacobian matrix :rtype: ndarray(m,n) - Computes a numerical approximation to the Jacobian for ``f(x)`` where + Computes a numerical approximation to the Jacobian for ``f(x)`` where :math:`f: \mathbb{R}^n \mapsto \mathbb{R}^m`. Uses first-order difference :math:`J[:,i] = (f(x + dx) - f(x)) / dx`. @@ -31,14 +32,14 @@ def jacobian_numerical(f, x, dx=1e-8, N=0): I = np.eye(len(x)) f0 = f(x) for i in range(len(x)): - fi = f(x + I[:,i] * dx) + fi = f(x + I[:, i] * dx) Ji = (fi - f0) / dx if N > 0: - t = Ji[:N,N] - r = base.vex(Ji[:N,:N] @ J0[:N,:N].T) + t = Ji[:N, N] + r = base.vex(Ji[:N, :N] @ J0[:N, :N].T) Ji = np.r_[t, r] - + Jcol.append(Ji) return np.c_[Jcol].T @@ -57,7 +58,7 @@ def hessian_numerical(J, x, dx=1e-8): :return: Hessian matrix :rtype: ndarray(m,n,n) - Computes a numerical approximation to the Hessian for ``J(x)`` where + Computes a numerical approximation to the Hessian for ``J(x)`` where :math:`f: \mathbb{R}^n \mapsto \mathbb{R}^{m \times n}` Uses first-order difference :math:`H[:,:,i] = (J(x + dx) - J(x)) / dx`. @@ -67,19 +68,21 @@ def hessian_numerical(J, x, dx=1e-8): Hcol = [] J0 = J(x) for i in range(len(x)): - - Ji = J(x + I[:,i] * dx) + Ji = J(x + I[:, i] * dx) Hi = (Ji - J0) / dx Hcol.append(Hi) - + return np.stack(Hcol, axis=2) if __name__ == "__main__": - import roboticstoolbox as rtb - np.set_printoptions(linewidth=120, formatter={'float': lambda x: f"{x:8.4g}" if abs(x) > 1e-10 else f"{0:8.4g}"}) + + np.set_printoptions( + linewidth=120, + formatter={"float": lambda x: f"{x:8.4g}" if abs(x) > 1e-10 else f"{0:8.4g}"}, + ) robot = rtb.models.DH.Puma560() q = robot.qn @@ -90,4 +93,4 @@ def hessian_numerical(J, x, dx=1e-8): H = hessian_numerical(robot.jacob0, q) print(H) - print(robot.ets().hessian0(q)) \ No newline at end of file + print(robot.ets().hessian0(q)) diff --git a/roboticstoolbox/tools/p_servo.py b/roboticstoolbox/tools/p_servo.py index d0c3e24ec..16fa8f33b 100644 --- a/roboticstoolbox/tools/p_servo.py +++ b/roboticstoolbox/tools/p_servo.py @@ -10,7 +10,6 @@ def angle_axis(T, Td): - try: e = Angle_Axis(T, Td) except BaseException: diff --git a/roboticstoolbox/tools/params.py b/roboticstoolbox/tools/params.py index 3f0766357..d2f336a41 100644 --- a/roboticstoolbox/tools/params.py +++ b/roboticstoolbox/tools/params.py @@ -6,5 +6,6 @@ def rtb_set_param(param, value): _params[param] = value + def rtb_get_param(param): - return _params[param] \ No newline at end of file + return _params[param] diff --git a/roboticstoolbox/tools/trajectory.py b/roboticstoolbox/tools/trajectory.py index 1cff59ad7..b12e5cda9 100644 --- a/roboticstoolbox/tools/trajectory.py +++ b/roboticstoolbox/tools/trajectory.py @@ -6,8 +6,6 @@ from spatialmath.base.argcheck import ( isvector, getvector, - # assertmatrix, - getvector, isscalar, ) @@ -244,7 +242,7 @@ def plot(self, block=False, plotargs=None, textargs=None): ax.set_xlim(0, max(self.t)) if self.istime: - ax.set_ylabel(f"$\ddot{{q}}(t)$", **textopts) + ax.set_ylabel("$\ddot{q}(t)$", **textopts) ax.set_xlabel("t (seconds)") else: ax.set_ylabel("$d^2q/dk^2$", **textopts) @@ -566,7 +564,6 @@ def trapezoidal_func(q0, qf, T, V=None): a = V / tb def trapezoidalfunc(t): - p = [] pd = [] pdd = [] @@ -966,7 +963,6 @@ def mstraj( raise ValueError("Length of TSEG does not match number of viapoints") if tsegment is None: - # This is unreachable, left just in case if qdmax is None: # pragma nocover raise ValueError("qdmax must be given if tsegment is not") @@ -1128,7 +1124,6 @@ def mrange(start, stop, step): if __name__ == "__main__": - # t = quintic(0, 1, 50) # t.plot() diff --git a/roboticstoolbox/tools/urdf/__init__.py b/roboticstoolbox/tools/urdf/__init__.py index 82bb29eaa..2e3fe895f 100644 --- a/roboticstoolbox/tools/urdf/__init__.py +++ b/roboticstoolbox/tools/urdf/__init__.py @@ -1,15 +1,45 @@ from roboticstoolbox.tools.urdf.urdf import ( URDFType, - Box, Cylinder, Sphere, Mesh, Geometry, - Collision, Visual, Inertial, - JointCalibration, JointDynamics, JointLimit, JointMimic, - SafetyController, Actuator, TransmissionJoint, - Transmission, Joint, Link, URDF) + Box, + Cylinder, + Sphere, + Mesh, + Geometry, + Collision, + Visual, + Inertial, + JointCalibration, + JointDynamics, + JointLimit, + JointMimic, + SafetyController, + Actuator, + TransmissionJoint, + Transmission, + Joint, + Link, + URDF, +) __all__ = [ - 'URDFType', 'Box', 'Cylinder', 'Sphere', 'Mesh', 'Geometry', - 'Collision', 'Visual', 'Inertial', - 'JointCalibration', 'JointDynamics', 'JointLimit', 'JointMimic', - 'SafetyController', 'Actuator', 'TransmissionJoint', - 'Transmission', 'Joint', 'Link', 'URDF' + "URDFType", + "Box", + "Cylinder", + "Sphere", + "Mesh", + "Geometry", + "Collision", + "Visual", + "Inertial", + "JointCalibration", + "JointDynamics", + "JointLimit", + "JointMimic", + "SafetyController", + "Actuator", + "TransmissionJoint", + "Transmission", + "Joint", + "Link", + "URDF", ] diff --git a/roboticstoolbox/tools/urdf/tests/test_urdf.py b/roboticstoolbox/tools/urdf/tests/test_urdf.py index e11a0981e..e07f282d8 100644 --- a/roboticstoolbox/tools/urdf/tests/test_urdf.py +++ b/roboticstoolbox/tools/urdf/tests/test_urdf.py @@ -14,55 +14,48 @@ class TestURDF(unittest.TestCase): - def test_urdf_visuals(self): - urdf_string = xacro.main( - rtb_path_to_datafile("xacro/franka_description/robots/panda_arm_hand.urdf.xacro")) # noqa + rtb_path_to_datafile( + "xacro/franka_description/robots/panda_arm_hand.urdf.xacro" + ) + ) # noqa urdf = URDF.loadstr( urdf_string, - rtb_path_to_datafile("xacro/franka_description/robots/panda_arm_hand.urdf.xacro")) # noqa + rtb_path_to_datafile( + "xacro/franka_description/robots/panda_arm_hand.urdf.xacro" + ), + ) # noqa urdf.links[0].visuals[0].name = "Lonk" self.assertTrue(urdf.links[0].visuals[0].name == "Lonk") - self.assertTrue( - isinstance( - urdf.links[0].visuals[0].origin, - np.ndarray)) + self.assertTrue(isinstance(urdf.links[0].visuals[0].origin, np.ndarray)) urdf.links[0].visuals[0].geometry.box = rp.tools.urdf.Box([1, 2, 3]) self.assertTrue( - isinstance( - urdf.links[0].visuals[0].geometry.geometry, - rp.tools.urdf.Box)) + isinstance(urdf.links[0].visuals[0].geometry.geometry, rp.tools.urdf.Box) + ) - urdf.links[0].visuals[0].geometry.cylinder = \ - rp.tools.urdf.Cylinder(1, 2) + urdf.links[0].visuals[0].geometry.cylinder = rp.tools.urdf.Cylinder(1, 2) - urdf.links[0].visuals[0].geometry.sphere = \ - rp.tools.urdf.Sphere(2) + urdf.links[0].visuals[0].geometry.sphere = rp.tools.urdf.Sphere(2) nt.assert_array_almost_equal( - urdf.links[0].visuals[0].geometry.box.size, - [1, 2, 3]) + urdf.links[0].visuals[0].geometry.box.size, [1, 2, 3] + ) - self.assertEqual( - urdf.links[0].visuals[0].geometry.cylinder.radius, - 1) + self.assertEqual(urdf.links[0].visuals[0].geometry.cylinder.radius, 1) - self.assertEqual( - urdf.links[0].visuals[0].geometry.sphere.radius, - 2) + self.assertEqual(urdf.links[0].visuals[0].geometry.sphere.radius, 2) self.assertTrue( - isinstance( - urdf.links[0].visuals[0].geometry.mesh, - rp.tools.urdf.Mesh)) + isinstance(urdf.links[0].visuals[0].geometry.mesh, rp.tools.urdf.Mesh) + ) try: xacro.main("") - except BaseException: # lgtm [py/catch-base-exception] + except BaseException: # lgtm [py/catch-base-exception] pass def test_urdf_load(self): @@ -72,42 +65,49 @@ def test_urdf_load(self): try: xacro.main("") - except BaseException: # lgtm [py/catch-base-exception] + except BaseException: # lgtm [py/catch-base-exception] pass def test_urdf_collisions(self): - urdf_string = xacro.main( - rtb_path_to_datafile("xacro/franka_description/robots/panda_arm_hand.urdf.xacro")) # noqa + rtb_path_to_datafile( + "xacro/franka_description/robots/panda_arm_hand.urdf.xacro" + ) + ) # noqa urdf = URDF.loadstr( urdf_string, - rtb_path_to_datafile("xacro/franka_description/robots/panda_arm_hand.urdf.xacro")) # noqa + rtb_path_to_datafile( + "xacro/franka_description/robots/panda_arm_hand.urdf.xacro" + ), + ) # noqa urdf.links[0].collisions[0].name = "Lonk" self.assertTrue(urdf.links[0].collisions[0].name == "Lonk") - self.assertTrue( - isinstance( - urdf.links[0].collisions[0].origin, - np.ndarray)) + self.assertTrue(isinstance(urdf.links[0].collisions[0].origin, np.ndarray)) try: xacro.main("") - except BaseException: # lgtm [py/catch-base-exception] + except BaseException: # lgtm [py/catch-base-exception] pass def test_urdf_dynamics(self): - urdf_string = xacro.main( - rtb_path_to_datafile("xacro/franka_description/robots/panda_arm_hand.urdf.xacro")) # noqa + rtb_path_to_datafile( + "xacro/franka_description/robots/panda_arm_hand.urdf.xacro" + ) + ) # noqa urdf = URDF.loadstr( urdf_string, - rtb_path_to_datafile("xacro/franka_description/robots/panda_arm_hand.urdf.xacro")) # noqa + rtb_path_to_datafile( + "xacro/franka_description/robots/panda_arm_hand.urdf.xacro" + ), + ) # noqa self.assertEqual(urdf.joints[0].limit.effort, 87.0) self.assertEqual(urdf.joints[0].limit.velocity, 2.175) try: xacro.main("") - except BaseException: # lgtm [py/catch-base-exception] + except BaseException: # lgtm [py/catch-base-exception] pass diff --git a/roboticstoolbox/tools/urdf/urdf.py b/roboticstoolbox/tools/urdf/urdf.py index 2069af158..d9ac55f09 100644 --- a/roboticstoolbox/tools/urdf/urdf.py +++ b/roboticstoolbox/tools/urdf/urdf.py @@ -297,7 +297,6 @@ def filename(self): @filename.setter def filename(self, value): - global _base_path if value.startswith("package://"): @@ -1954,8 +1953,9 @@ def _validate_transmissions(self): for joint in t.joints: if joint.name not in self._joint_map: raise ValueError( - "Transmission {} has invalid joint name " - "{}".format(t.name, joint.name) + "Transmission {} has invalid joint name " "{}".format( + t.name, joint.name + ) ) @classmethod diff --git a/roboticstoolbox/tools/urdf/utils.py b/roboticstoolbox/tools/urdf/utils.py index fd0ff63f9..7098e8438 100644 --- a/roboticstoolbox/tools/urdf/utils.py +++ b/roboticstoolbox/tools/urdf/utils.py @@ -1,5 +1,5 @@ -"""Utilities for URDF parsing. -""" +"""Utilities for URDF parsing.""" + import numpy as np import spatialmath as sm @@ -21,13 +21,13 @@ def parse_origin(node): """ matrix = sm.SE3() rpy = np.array([0, 0, 0]) - origin_node = node.find('origin') + origin_node = node.find("origin") if origin_node is not None: - if 'xyz' in origin_node.attrib: - t = np.fromstring(origin_node.attrib['xyz'], sep=' ') + if "xyz" in origin_node.attrib: + t = np.fromstring(origin_node.attrib["xyz"], sep=" ") matrix = sm.SE3(t) - if 'rpy' in origin_node.attrib: - rpy = np.fromstring(origin_node.attrib['rpy'], sep=' ') + if "rpy" in origin_node.attrib: + rpy = np.fromstring(origin_node.attrib["rpy"], sep=" ") matrix.A[:3, :3] = sm.SE3.RPY(rpy).R return matrix.A, rpy diff --git a/roboticstoolbox/tools/xacro/__init__.py b/roboticstoolbox/tools/xacro/__init__.py index 7c616768a..770d122f0 100644 --- a/roboticstoolbox/tools/xacro/__init__.py +++ b/roboticstoolbox/tools/xacro/__init__.py @@ -42,8 +42,13 @@ from copy import deepcopy from .color import error, warning -from .xmlutils import opt_attrs, reqd_attrs, first_child_element, \ - next_sibling_element, replace_node +from .xmlutils import ( + opt_attrs, + reqd_attrs, + first_child_element, + next_sibling_element, + replace_node, +) _basestr = str @@ -58,7 +63,7 @@ filestack = [] # The top level directory -tld = '' +tld = "" def push_file(filename): @@ -89,18 +94,18 @@ def abs_filename_spec(filename_spec): """ if not os.path.isabs(filename_spec): parent_filename = filestack[-1] - basedir = os.path.dirname(parent_filename) if parent_filename else '.' + basedir = os.path.dirname(parent_filename) if parent_filename else "." return os.path.join(basedir, filename_spec) return filename_spec # pragma: no cover class YamlDictWrapper(dict): # pragma: no cover """Wrapper class providing dotted access to dict items""" + def __getattr__(self, item): try: result = super(YamlDictWrapper, self).__getitem__(item) - return YamlDictWrapper(result) \ - if isinstance(result, dict) else result + return YamlDictWrapper(result) if isinstance(result, dict) else result except KeyError: raise XacroException("No such key: '{}'".format(item)) @@ -131,16 +136,29 @@ def load_yaml(filename): # pragma: no cover # for discussion, see: # http://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html global_symbols = { - '__builtins__': { - k: __builtins__[k] for k in [ - 'list', 'dict', 'map', 'len', 'str', 'float', 'int', - 'True', 'False', 'min', 'max', 'round']}} + "__builtins__": { + k: __builtins__[k] + for k in [ + "list", + "dict", + "map", + "len", + "str", + "float", + "int", + "True", + "False", + "min", + "max", + "round", + ] + } +} # also define all math symbols and functions global_symbols.update(math.__dict__) # expose load_yaml and abs_filename -global_symbols.update( - dict(load_yaml=load_yaml, abs_filename=abs_filename_spec)) +global_symbols.update(dict(load_yaml=load_yaml, abs_filename=abs_filename_spec)) class XacroException(Exception): @@ -158,8 +176,9 @@ def __init__(self, msg=None, suffix=None, exc=None, macro=None): def __str__(self): # pragma: no cover items = [super(XacroException, self).__str__(), self.exc, self.suffix] - return ' '.join( - [s for s in [unicode(e) for e in items] if s not in ['', 'None']]) + return " ".join( + [s for s in [unicode(e) for e in items] if s not in ["", "None"]] + ) verbosity = 1 @@ -177,11 +196,12 @@ def check_attrs(tag, required, optional): result.extend(opt_attrs(tag, optional)) allowed = required + optional extra = [ - a for a in tag.attributes.keys() - if a not in allowed and not a.startswith("xmlns:")] + a + for a in tag.attributes.keys() + if a not in allowed and not a.startswith("xmlns:") + ] if extra: # pragma: no cover - warning( - "%s: unknown attribute(s): %s" % (tag.nodeName, ', '.join(extra))) + warning("%s: unknown attribute(s): %s" % (tag.nodeName, ", ".join(extra))) if verbosity > 0: print_location(filestack) return result @@ -196,27 +216,25 @@ def __init__(self): def eval_extension(s): # pragma: no cover - - if s == '$(cwd)': + if s == "$(cwd)": return os.getcwd() - if s.startswith('$(find'): + if s.startswith("$(find"): return tld # return '..' - if s.startswith('$(arg'): - s = s.replace('$(arg ', '') + if s.startswith("$(arg"): + s = s.replace("$(arg ", "") s = s[:-1] - return substitution_args_context['arg'][s] + return substitution_args_context["arg"][s] try: from roslaunch.substitution_args import resolve_args, ArgException from rospkg.common import ResourceNotFound - return resolve_args( - s, context=substitution_args_context, resolve_anon=False) - except ImportError: - return '' + return resolve_args(s, context=substitution_args_context, resolve_anon=False) + except ImportError: + return "" # raise XacroException("substitution args not supported: ", exc=e) except ArgException as e: raise XacroException("Undefined substitution argument", exc=e) @@ -247,7 +265,7 @@ def _eval_literal(value): # (due to PEP515). # Here, we want to handle literals with underscores as plain # strings. - if '_' in value: + if "_" in value: return value # order of types is important! for f in [int, float, lambda x: get_boolean_value(x, None)]: @@ -261,20 +279,26 @@ def _resolve_(self, key): # lazy evaluation if key in self.unevaluated: if key in self.recursive: - raise XacroException("recursive variable definition: %s" % - " -> ".join(self.recursive + [key])) + raise XacroException( + "recursive variable definition: %s" + % " -> ".join(self.recursive + [key]) + ) self.recursive.append(key) - self.table[key] = self._eval_literal( - eval_text(self.table[key], self)) + self.table[key] = self._eval_literal(eval_text(self.table[key], self)) self.unevaluated.remove(key) self.recursive.remove(key) # return evaluated result value = self.table[key] - if (verbosity > 2 and self.parent is None) or verbosity > 3: # pragma: no cover # noqa - print("{indent}use {key}: {value} ({loc})".format( - indent=self.depth * ' ', key=key, value=value, - loc=filestack[-1]), file=sys.stderr) + if ( + verbosity > 2 and self.parent is None + ) or verbosity > 3: # pragma: no cover # noqa + print( + "{indent}use {key}: {value} ({loc})".format( + indent=self.depth * " ", key=key, value=value, loc=filestack[-1] + ), + file=sys.stderr, + ) return value def __getitem__(self, key): @@ -295,23 +319,26 @@ def _setitem(self, key, value, unevaluated): if unevaluated and isinstance(value, _basestr): # literal evaluation failed: re-evaluate lazily at first access self.unevaluated.add(key) - elif key in self.unevaluated: # pragma: no cover + elif key in self.unevaluated: # pragma: no cover # all other types cannot be evaluated self.unevaluated.remove(key) - if (verbosity > 2 and self.parent is None) or verbosity > 3: # pragma: no cover # noqa - print("{indent}set {key}: {value} ({loc})".format( - indent=self.depth * ' ', key=key, value=value, - loc=filestack[-1]), file=sys.stderr) + if ( + verbosity > 2 and self.parent is None + ) or verbosity > 3: # pragma: no cover # noqa + print( + "{indent}set {key}: {value} ({loc})".format( + indent=self.depth * " ", key=key, value=value, loc=filestack[-1] + ), + file=sys.stderr, + ) def __setitem__(self, key, value): self._setitem(key, value, unevaluated=True) def __contains__(self, key): - return \ - key in self.table or \ - (self.parent and key in self.parent) + return key in self.table or (self.parent and key in self.parent) - def __str__(self): # pragma: no cover + def __str__(self): # pragma: no cover s = unicode(self.table) if isinstance(self.parent, Table): s += "\n parent: " @@ -372,27 +399,27 @@ def next(self): m = self.res[i].match(self.str) if m: self.top = (i, m.group(0)) - self.str = self.str[m.end():] + self.str = self.str[m.end() :] return result - raise XacroException('invalid expression: ' + self.str) + raise XacroException("invalid expression: " + self.str) all_includes = [] -include_no_matches_msg = \ - """Include tag's filename spec \"{}\" matched "\" no files.""" +include_no_matches_msg = """Include tag's filename spec \"{}\" matched "\" no files.""" def get_include_files(filename_spec, symbols): try: filename_spec = abs_filename_spec(eval_text(filename_spec, symbols)) - except XacroException as e: # pragma: no cover + except XacroException as e: # pragma: no cover if e.exc and isinstance(e.exc, NameError) and symbols is None: raise XacroException( - 'variable filename is supported with in-order option only') + "variable filename is supported with in-order option only" + ) else: raise - if re.search('[*[?]+', filename_spec): # pragma: no cover + if re.search("[*[?]+", filename_spec): # pragma: no cover # Globbing behaviour filenames = sorted(glob.glob(filename_spec)) if len(filenames) == 0: @@ -410,13 +437,15 @@ def get_include_files(filename_spec, symbols): def import_xml_namespaces(parent, attributes): """import all namespace declarations into parent""" for name, value in attributes.items(): - if name.startswith('xmlns:'): + if name.startswith("xmlns:"): oldAttr = parent.getAttributeNode(name) if oldAttr and oldAttr.value != value: - warning("inconsistent namespace redefinitions for {name}:" # pragma: no cover # noqa - "\n old: {old}\n new: {new} ({new_file})".format( - name=name, old=oldAttr.value, new=value, - new_file=filestack[-1])) + warning( + "inconsistent namespace redefinitions for {name}:" # pragma: no cover # noqa + "\n old: {old}\n new: {new} ({new_file})".format( + name=name, old=oldAttr.value, new=value, new_file=filestack[-1] + ) + ) else: parent.setAttribute(name, value) @@ -424,7 +453,8 @@ def import_xml_namespaces(parent, attributes): def process_include(elt, macros, symbols, func): included = [] filename_spec, namespace_spec, optional = check_attrs( - elt, ['filename'], ['ns', 'optional']) + elt, ["filename"], ["ns", "optional"] + ) if namespace_spec: try: namespace_spec = eval_text(namespace_spec, symbols) @@ -432,15 +462,16 @@ def process_include(elt, macros, symbols, func): symbols[namespace_spec] = ns_symbols = PropertyNameSpace() macros = ns_macros symbols = ns_symbols - except TypeError: # pragma: no cover - raise XacroException( - 'namespaces are supported with in-order option only') + except TypeError: # pragma: no cover + raise XacroException("namespaces are supported with in-order option only") optional = get_boolean_value(optional, None) if first_child_element(elt): - warning("Child elements of a tag are ignored") # pragma: no cover # noqa - if verbosity > 0: # pragma: no cover + warning( + "Child elements of a tag are ignored" + ) # pragma: no cover # noqa + if verbosity > 0: # pragma: no cover print_location(filestack) for filename in get_include_files(filename_spec, symbols): @@ -477,10 +508,13 @@ def is_valid_name(name): try: root = ast.parse(name) - if isinstance(root, ast.Module) and \ - len(root.body) == 1 and isinstance(root.body[0], ast.Expr) and \ - isinstance( - root.body[0].value, ast.Name) and root.body[0].value.id == name: + if ( + isinstance(root, ast.Module) + and len(root.body) == 1 + and isinstance(root.body[0], ast.Expr) + and isinstance(root.body[0].value, ast.Name) + and root.body[0].value.id == name + ): return True except SyntaxError: pass @@ -488,7 +522,9 @@ def is_valid_name(name): return False -re_macro_arg = re.compile(r'''\s*([^\s:=]+?):?=(\^\|?)?((?:(?:'[^']*')?[^\s'"]*?)*)(?:\s+|$)(.*)''') # noqa +re_macro_arg = re.compile( + r"""\s*([^\s:=]+?):?=(\^\|?)?((?:(?:'[^']*')?[^\s'"]*?)*)(?:\s+|$)(.*)""" +) # noqa # space param := ^| <-- default --> space rest # noqa @@ -514,21 +550,21 @@ def parse_macro_arg(s): else: # there is no default specified at all result = s.lstrip().split(None, 1) - return result[0], None, result[1] if len(result) > 1 else '' + return result[0], None, result[1] if len(result) > 1 else "" def grab_macro(elt, macros): - assert(elt.tagName == 'xacro:macro') + assert elt.tagName == "xacro:macro" remove_previous_comments(elt) - name, params = check_attrs(elt, ['name'], ['params']) - if name == 'call': + name, params = check_attrs(elt, ["name"], ["params"]) + if name == "call": raise XacroException("Invalid use of macro name 'call'") - if name.find('.') != -1: # pragma: no cover + if name.find(".") != -1: # pragma: no cover raise XacroException( - "macro names must not contain '.' (reserved for " - "namespaces): %s" % name) - if name.startswith('xacro:'): + "macro names must not contain '.' (reserved for " "namespaces): %s" % name + ) + if name.startswith("xacro:"): warning("macro names must not contain prefix 'xacro:': %s" % name) name = name[6:] # drop 'xacro:' prefix @@ -552,43 +588,45 @@ def grab_macro(elt, macros): def grab_property(elt, table): - assert(elt.tagName == 'xacro:property') + assert elt.tagName == "xacro:property" remove_previous_comments(elt) name, value, default, scope = check_attrs( - elt, ['name'], ['value', 'default', 'scope']) + elt, ["name"], ["value", "default", "scope"] + ) if not is_valid_name(name): + raise XacroException("Property names must be valid python identifiers: " + name) + if value is not None and default is not None: # pragma: no cover raise XacroException( - 'Property names must be valid python identifiers: ' + name) - if value is not None and default is not None: # pragma: no cover - raise XacroException( - 'Property cannot define both a default and a value: ' + name) + "Property cannot define both a default and a value: " + name + ) if default is not None: - if scope is not None: # pragma: no cover + if scope is not None: # pragma: no cover warning( "%s: default property value can only be defined " - "on local scope" % name) + "on local scope" % name + ) if name not in table: value = default - else: # pragma: no cover + else: # pragma: no cover replace_node(elt, by=None) return if value is None: - name = '**' + name + name = "**" + name value = elt # debug replace_node(elt, by=None) - if scope and scope == 'global': + if scope and scope == "global": target_table = table.root() unevaluated = False - elif scope and scope == 'parent': + elif scope and scope == "parent": if table.parent: target_table = table.parent unevaluated = False - else: # pragma: no cover + else: # pragma: no cover warning("%s: no parent scope at global scope " % name) return # cannot store the value, no reason to evaluate it else: @@ -604,10 +642,11 @@ def grab_property(elt, table): LEXER = QuickLexer( # multiple $ in a row, followed by { or ( DOLLAR_DOLLAR_BRACE=r"^\$\$+(\{|\()", - EXPR=r"^\$\{[^\}]*\}", # stuff starting with ${ - EXTENSION=r"^\$\([^\)]*\)", # stuff starting with $( + EXPR=r"^\$\{[^\}]*\}", # stuff starting with ${ + EXTENSION=r"^\$\([^\)]*\)", # stuff starting with $( # any text w/o $ or $ following any chars except {($ or single $ - TEXT=r"[^$]+|\$[^{($]+|\$$") + TEXT=r"[^$]+|\$[^{($]+|\$$", +) # evaluate text and return typed value @@ -617,11 +656,11 @@ def handle_expr(s): return eval(eval_text(s, symbols), global_symbols, symbols) except Exception as e: # re-raise as XacroException to add more context - raise XacroException(exc=e, - suffix=os.linesep - + "when evaluating expression '%s'" % s) + raise XacroException( + exc=e, suffix=os.linesep + "when evaluating expression '%s'" % s + ) - def handle_extension(s): # pragma: no cover + def handle_extension(s): # pragma: no cover return eval_extension("$(%s)" % eval_text(s, symbols)) results = [] @@ -631,7 +670,7 @@ def handle_extension(s): # pragma: no cover id = lex.peek()[0] if id == lex.EXPR: results.append(handle_expr(lex.next()[1][2:-1])) - elif id == lex.EXTENSION: # pragma: no cover + elif id == lex.EXTENSION: # pragma: no cover results.append(handle_extension(lex.next()[1][2:-1])) elif id == lex.TEXT: results.append(lex.next()[1]) @@ -642,7 +681,7 @@ def handle_extension(s): # pragma: no cover return results[0] # otherwise join elements to a string else: - return ''.join(map(unicode, results)) + return "".join(map(unicode, results)) def eval_default_arg(forward_variable, default, symbols, macro): @@ -650,24 +689,24 @@ def eval_default_arg(forward_variable, default, symbols, macro): return eval_text(default, symbols) try: return symbols[forward_variable] - except KeyError: # pragma: no cover + except KeyError: # pragma: no cover if default is not None: return eval_text(default, symbols) else: raise XacroException( - "Undefined property to forward: " - + forward_variable, macro=macro) + "Undefined property to forward: " + forward_variable, macro=macro + ) def handle_dynamic_macro_call(node, macros, symbols): - name, = reqd_attrs(node, ['macro']) - if not name: # pragma: no cover + (name,) = reqd_attrs(node, ["macro"]) + if not name: # pragma: no cover raise XacroException("xacro:call is missing the 'macro' attribute") name = unicode(eval_text(name, symbols)) # remove 'macro' attribute and rename tag with resolved macro name - node.removeAttribute('macro') - node.tagName = 'xacro:' + name + node.removeAttribute("macro") + node.tagName = "xacro:" + name # forward to handle_macro_call handle_macro_call(node, macros, symbols) return True @@ -675,7 +714,7 @@ def handle_dynamic_macro_call(node, macros, symbols): def resolve_macro(fullname, macros): # split name into namespaces and real name - namespaces = fullname.split('.') + namespaces = fullname.split(".") name = namespaces.pop(-1) def _resolve(namespaces, name, macros): @@ -695,9 +734,9 @@ def _resolve(namespaces, name, macros): def handle_macro_call(node, macros, symbols): - if node.tagName == 'xacro:call': + if node.tagName == "xacro:call": return handle_dynamic_macro_call(node, macros, symbols) - elif not node.tagName.startswith('xacro:'): + elif not node.tagName.startswith("xacro:"): return False # no macro name = node.tagName[6:] # drop 'xacro:' prefix @@ -712,9 +751,8 @@ def handle_macro_call(node, macros, symbols): scoped = Table(symbols) # new local name space for macro evaluation params = m.params[:] # deep copy macro's params list for name, value in node.attributes.items(): - if name not in params: # pragma: no cover - raise XacroException( - "Invalid parameter \"%s\"" % unicode(name), macro=m) + if name not in params: # pragma: no cover + raise XacroException('Invalid parameter "%s"' % unicode(name), macro=m) params.remove(name) scoped._setitem(name, eval_text(value, symbols), unevaluated=False) node.setAttribute(name, "") # suppress second evaluation in eval_all() @@ -725,39 +763,38 @@ def handle_macro_call(node, macros, symbols): # Fetch block parameters, in order block = first_child_element(node) for param in params[:]: - if param[0] == '*': - if not block: # pragma: no cover + if param[0] == "*": + if not block: # pragma: no cover raise XacroException("Not enough blocks", macro=m) params.remove(param) scoped[param] = block block = next_sibling_element(block) - if block is not None: # pragma: no cover - raise XacroException("Unused block \"%s\"" % block.tagName, macro=m) + if block is not None: # pragma: no cover + raise XacroException('Unused block "%s"' % block.tagName, macro=m) # Try to load defaults for any remaining non-block parameters for param in params[:]: # block parameters are not supported for defaults - if param[0] == '*': # pragma: no cover + if param[0] == "*": # pragma: no cover continue # get default name, default = m.defaultmap.get(param, (None, None)) if name is not None or default is not None: scoped._setitem( - param, eval_default_arg( - name, default, symbols, m), unevaluated=False) + param, eval_default_arg(name, default, symbols, m), unevaluated=False + ) params.remove(param) if params: - raise XacroException( - "Undefined parameters [%s]" % ",".join(params), macro=m) + raise XacroException("Undefined parameters [%s]" % ",".join(params), macro=m) try: eval_all(body, macros, scoped) - except Exception as e: # pragma: no cover + except Exception as e: # pragma: no cover # fill in macro call history for nice error reporting - if hasattr(e, 'macros'): + if hasattr(e, "macros"): e.macros.append(m) else: e.macros = [m] @@ -784,9 +821,9 @@ def get_boolean_value(value, condition): """ try: if isinstance(value, _basestr): - if value == 'true' or value == 'True': + if value == "true" or value == "True": return True - elif value == 'false' or value == 'False': + elif value == "false" or value == "False": return False else: return bool(int(value)) @@ -794,12 +831,16 @@ def get_boolean_value(value, condition): return bool(value) except Exception: raise XacroException( - "Xacro conditional \"%s\" evaluated to \"%s\", " - "which is not a boolean expression." % (condition, value)) + 'Xacro conditional "%s" evaluated to "%s", ' + "which is not a boolean expression." % (condition, value) + ) -_empty_text_node = xml.dom.minidom.getDOMImplementation().createDocument( - None, "dummy", None).createTextNode('\n\n') +_empty_text_node = ( + xml.dom.minidom.getDOMImplementation() + .createDocument(None, "dummy", None) + .createTextNode("\n\n") +) def remove_previous_comments(node): @@ -807,8 +848,11 @@ def remove_previous_comments(node): next = node.nextSibling previous = node.previousSibling while previous: - if previous.nodeType == xml.dom.Node.TEXT_NODE and \ - previous.data.isspace() and previous.data.count('\n') <= 1: + if ( + previous.nodeType == xml.dom.Node.TEXT_NODE + and previous.data.isspace() + and previous.data.count("\n") <= 1 + ): # skip a single empty text node (max 1 newline) previous = previous.previousSibling @@ -832,7 +876,7 @@ def eval_all(node, macros, symbols): """ # evaluate the attributes for name, value in node.attributes.items(): - if name.startswith('xacro:'): # remove xacro:* attributes + if name.startswith("xacro:"): # remove xacro:* attributes node.removeAttribute(name) else: result = unicode(eval_text(value, symbols)) @@ -840,7 +884,7 @@ def eval_all(node, macros, symbols): # remove xacro namespace definition try: - node.removeAttribute('xmlns:xacro') + node.removeAttribute("xmlns:xacro") except xml.dom.NotFoundErr: pass @@ -848,19 +892,19 @@ def eval_all(node, macros, symbols): while node: next = node.nextSibling if node.nodeType == xml.dom.Node.ELEMENT_NODE: - if node.tagName == 'xacro:insert_block': - name, = check_attrs(node, ['name'], []) + if node.tagName == "xacro:insert_block": + (name,) = check_attrs(node, ["name"], []) if ("**" + name) in symbols: # Multi-block - block = symbols['**' + name] + block = symbols["**" + name] content_only = True elif ("*" + name) in symbols: # Single block - block = symbols['*' + name] + block = symbols["*" + name] content_only = False - else: # pragma: no cover - raise XacroException("Undefined block \"%s\"" % name) + else: # pragma: no cover + raise XacroException('Undefined block "%s"' % name) # cloning block allows to insert the same block multiple times block = block.cloneNode(deep=True) @@ -868,48 +912,47 @@ def eval_all(node, macros, symbols): eval_all(block, macros, symbols) replace_node(node, by=block, content_only=content_only) - elif node.tagName == 'xacro:include': + elif node.tagName == "xacro:include": process_include(node, macros, symbols, eval_all) - elif node.tagName == 'xacro:property': + elif node.tagName == "xacro:property": grab_property(node, symbols) - elif node.tagName == 'xacro:macro': + elif node.tagName == "xacro:macro": grab_macro(node, macros) - elif node.tagName == 'xacro:arg': - name, default = check_attrs(node, ['name', 'default'], []) - if name not in substitution_args_context['arg']: - substitution_args_context['arg'][name] = eval_text( - default, symbols) + elif node.tagName == "xacro:arg": + name, default = check_attrs(node, ["name", "default"], []) + if name not in substitution_args_context["arg"]: + substitution_args_context["arg"][name] = eval_text(default, symbols) remove_previous_comments(node) replace_node(node, by=None) - elif node.tagName == 'xacro:element': - name = eval_text( - *reqd_attrs(node, ['xacro:name']), symbols=symbols) - if not name: # pragma: no cover + elif node.tagName == "xacro:element": + name = eval_text(*reqd_attrs(node, ["xacro:name"]), symbols=symbols) + if not name: # pragma: no cover raise XacroException("xacro:element: empty name") - node.removeAttribute('xacro:name') + node.removeAttribute("xacro:name") node.nodeName = node.tagName = name continue # re-process the node with new tagName - elif node.tagName == 'xacro:attribute': - name, value = [eval_text(a, symbols) for a in reqd_attrs( - node, ['name', 'value'])] - if not name: # pragma: no cover + elif node.tagName == "xacro:attribute": + name, value = [ + eval_text(a, symbols) for a in reqd_attrs(node, ["name", "value"]) + ] + if not name: # pragma: no cover raise XacroException("xacro:attribute: empty name") node.parentNode.setAttribute(name, value) replace_node(node, by=None) - elif node.tagName in ['xacro:if', 'xacro:unless']: + elif node.tagName in ["xacro:if", "xacro:unless"]: remove_previous_comments(node) - cond, = check_attrs(node, ['value'], []) + (cond,) = check_attrs(node, ["value"], []) keep = get_boolean_value(eval_text(cond, symbols), cond) - if node.tagName in ['unless', 'xacro:unless']: + if node.tagName in ["unless", "xacro:unless"]: keep = not keep if keep: @@ -954,9 +997,9 @@ def parse(inp, filename=None): try: if isinstance(inp, _basestr): return xml.dom.minidom.parseString(inp) - elif hasattr(inp, 'read'): + elif hasattr(inp, "read"): return xml.dom.minidom.parse(inp) - return inp # pragma: no cover + return inp # pragma: no cover finally: if f: @@ -965,10 +1008,10 @@ def parse(inp, filename=None): def process_doc(doc, mappings=None, **kwargs): global verbosity - verbosity = kwargs.get('verbosity', verbosity) + verbosity = kwargs.get("verbosity", verbosity) # set substitution args - substitution_args_context['arg'] = {} if mappings is None else mappings + substitution_args_context["arg"] = {} if mappings is None else mappings # if not yet defined: initialize filestack if not filestack: @@ -978,21 +1021,21 @@ def process_doc(doc, mappings=None, **kwargs): symbols = Table() # apply xacro:targetNamespace as global xmlns (if defined) - targetNS = doc.documentElement.getAttribute('xacro:targetNamespace') + targetNS = doc.documentElement.getAttribute("xacro:targetNamespace") if targetNS: - doc.documentElement.removeAttribute('xacro:targetNamespace') - doc.documentElement.setAttribute('xmlns', targetNS) + doc.documentElement.removeAttribute("xacro:targetNamespace") + doc.documentElement.setAttribute("xmlns", targetNS) eval_all(doc.documentElement, macros, symbols) # reset substitution args - substitution_args_context['arg'] = {} + substitution_args_context["arg"] = {} def open_output(output_filename): - if output_filename is None: # pragma: no cover + if output_filename is None: # pragma: no cover return sys.stdout - else: # pragma: no cover + else: # pragma: no cover dir_name = os.path.dirname(output_filename) if dir_name: try: @@ -1004,29 +1047,29 @@ def open_output(output_filename): pass try: - return open(output_filename, 'w') + return open(output_filename, "w") except IOError as e: raise XacroException("Failed to open output:", exc=e) def print_location(filestack, err=None, file=sys.stderr): - macros = getattr(err, 'macros', []) if err else [] - msg = 'when instantiating macro:' - for m in macros: # pragma: no cover - name = m.body.getAttribute('name') - location = '(%s)' % m.history[-1][-1] + macros = getattr(err, "macros", []) if err else [] + msg = "when instantiating macro:" + for m in macros: # pragma: no cover + name = m.body.getAttribute("name") + location = "(%s)" % m.history[-1][-1] print(msg, name, location, file=file) - msg = 'instantiated from:' + msg = "instantiated from:" - msg = 'in file:' if macros else 'when processing file:' + msg = "in file:" if macros else "when processing file:" for f in reversed(filestack): if f is None: - f = 'string' + f = "string" print(msg, f, file=file) - msg = 'included from:' + msg = "included from:" -def process_file(input_file_name, **kwargs): # pragma: no cover +def process_file(input_file_name, **kwargs): # pragma: no cover """main processing pipeline""" # initialize file stack for error-reporting restore_filestack([input_file_name]) @@ -1036,13 +1079,16 @@ def process_file(input_file_name, **kwargs): # pragma: no cover process_doc(doc, **kwargs) # add xacro auto-generated banner - banner = [xml.dom.minidom.Comment(c) for c in - [" %s " % ('=' * 83), - " | This document was autogenerated by xacro from %-30s | " - % input_file_name, - " | EDITING THIS FILE BY HAND IS NOT RECOMMENDED %-30s | " - % "", - " %s " % ('=' * 83)]] + banner = [ + xml.dom.minidom.Comment(c) + for c in [ + " %s " % ("=" * 83), + " | This document was autogenerated by xacro from %-30s | " + % input_file_name, + " | EDITING THIS FILE BY HAND IS NOT RECOMMENDED %-30s | " % "", + " %s " % ("=" * 83), + ] + ] first = doc.firstChild for comment in banner: doc.insertBefore(comment, first) @@ -1050,20 +1096,20 @@ def process_file(input_file_name, **kwargs): # pragma: no cover return doc -def main(filename, tld_other=None): # pragma: no cover +def main(filename, tld_other=None): # pragma: no cover opts = { - 'output': None, - 'just_deps': False, - 'in_order': True, - 'verbosity': 1, - 'just_includes': False, - 'mappings': {} + "output": None, + "just_deps": False, + "in_order": True, + "verbosity": 1, + "just_includes": False, + "mappings": {}, } global tld if tld_other is None: - tld = '..' + tld = ".." else: tld = tld_other @@ -1099,4 +1145,4 @@ def main(filename, tld_other=None): # pragma: no cover if opts["output"]: out.close() - return doc.toprettyxml(indent=' ', **encoding) + return doc.toprettyxml(indent=" ", **encoding) diff --git a/roboticstoolbox/tools/xacro/cli.py b/roboticstoolbox/tools/xacro/cli.py index 29b3c26e4..6a5722be0 100644 --- a/roboticstoolbox/tools/xacro/cli.py +++ b/roboticstoolbox/tools/xacro/cli.py @@ -35,9 +35,9 @@ from .color import colorize, message -class ColoredOptionParser(OptionParser): # pragma: no cover +class ColoredOptionParser(OptionParser): # pragma: no cover def error(self, message): - msg = colorize(message, 'red') + msg = colorize(message, "red") OptionParser.error(self, msg) @@ -46,7 +46,7 @@ def error(self, message): def wrap_with_newlines(text, width, **kwargs): # pragma: no cover result = [] - for paragraph in text.split('\n'): + for paragraph in text.split("\n"): result.extend(_original_wrap(paragraph, width, **kwargs)) return result @@ -62,31 +62,49 @@ def format_option(self, text): return result -def process_args(argv, require_input=True): # pragma: no cover - parser = ColoredOptionParser(usage="usage: %prog [options] ", - formatter=IndentedHelpFormatterWithNL()) - parser.add_option("-o", dest="output", metavar="FILE", - help="write output to FILE instead of stdout") - parser.add_option("--deps", action="store_true", dest="just_deps", - help="print file dependencies") +def process_args(argv, require_input=True): # pragma: no cover + parser = ColoredOptionParser( + usage="usage: %prog [options] ", formatter=IndentedHelpFormatterWithNL() + ) parser.add_option( - "--inorder", "-i", action="store_true", dest="in_order", - help="processing in read order (default, can be omitted)") + "-o", + dest="output", + metavar="FILE", + help="write output to FILE instead of stdout", + ) + parser.add_option( + "--deps", action="store_true", dest="just_deps", help="print file dependencies" + ) + parser.add_option( + "--inorder", + "-i", + action="store_true", + dest="in_order", + help="processing in read order (default, can be omitted)", + ) # verbosity options - parser.add_option("-q", action="store_const", dest="verbosity", const=0, - help="quiet operation, suppressing warnings") - parser.add_option("-v", action="count", dest="verbosity", - help="increase verbosity") - parser.add_option("--verbosity", metavar='level', dest="verbosity", - type='int', - help=textwrap.dedent("""\ + parser.add_option( + "-q", + action="store_const", + dest="verbosity", + const=0, + help="quiet operation, suppressing warnings", + ) + parser.add_option("-v", action="count", dest="verbosity", help="increase verbosity") + parser.add_option( + "--verbosity", + metavar="level", + dest="verbosity", + type="int", + help=textwrap.dedent("""\ set verbosity level 0: quiet, suppressing warnings 1: default, showing warnings and error locations 2: show stack trace 3: log property definitions and usage on top level - 4: log property definitions and usage on all levels""")) + 4: log property definitions and usage on all levels"""), + ) mappings = {} filtered_args = argv @@ -96,7 +114,8 @@ def process_args(argv, require_input=True): # pragma: no cover if options.in_order: message( "xacro: in-order processing became default in ROS Melodic. " - "You can drop the option.") + "You can drop the option." + ) options.in_order = True if len(pos_args) != 1: diff --git a/roboticstoolbox/tools/xacro/color.py b/roboticstoolbox/tools/xacro/color.py index 0e636b416..83e5d57bb 100644 --- a/roboticstoolbox/tools/xacro/color.py +++ b/roboticstoolbox/tools/xacro/color.py @@ -30,37 +30,37 @@ import sys # bold colors -_ansi = {'red': 91, 'yellow': 93} +_ansi = {"red": 91, "yellow": 93} def is_tty(stream): # taken from catkin_tools/common.py # pragma: no cover """Returns True if the given stream is a tty, else False""" - return hasattr(stream, 'isatty') and stream.isatty() + return hasattr(stream, "isatty") and stream.isatty() def colorize(msg, color, file=sys.stderr, alt_text=None): # pragma: no cover if color and is_tty(file): - return '\033[%dm%s\033[0m' % (_ansi[color], msg) + return "\033[%dm%s\033[0m" % (_ansi[color], msg) elif alt_text: - return '%s%s' % (alt_text, msg) + return "%s%s" % (alt_text, msg) else: return msg def message(msg, *args, **kwargs): # pragma: no cover - file = kwargs.get('file', sys.stderr) - alt_text = kwargs.get('alt_text', None) - color = kwargs.get('color', None) + file = kwargs.get("file", sys.stderr) + alt_text = kwargs.get("alt_text", None) + color = kwargs.get("color", None) print(colorize(msg, color, file, alt_text), *args, file=file) def warning(*args, **kwargs): # pragma: no cover - defaults = dict(file=sys.stderr, alt_text='warning: ', color='yellow') + defaults = dict(file=sys.stderr, alt_text="warning: ", color="yellow") defaults.update(kwargs) message(*args, **defaults) def error(*args, **kwargs): # pragma: no cover - defaults = dict(file=sys.stderr, alt_text='error: ', color='red') + defaults = dict(file=sys.stderr, alt_text="error: ", color="red") defaults.update(kwargs) message(*args, **defaults) diff --git a/roboticstoolbox/tools/xacro/tests/test_xacro.py b/roboticstoolbox/tools/xacro/tests/test_xacro.py index 7525c3655..f0224c4fa 100644 --- a/roboticstoolbox/tools/xacro/tests/test_xacro.py +++ b/roboticstoolbox/tools/xacro/tests/test_xacro.py @@ -53,27 +53,24 @@ from io import StringIO # Python 3.x # regex to match whitespace -whitespace = re.compile(r'\s+') +whitespace = re.compile(r"\s+") def text_values_match(a, b): # generic comparison - if whitespace.sub(' ', a).strip() == whitespace.sub(' ', b).strip(): + if whitespace.sub(" ", a).strip() == whitespace.sub(" ", b).strip(): return True try: # special handling of dicts: ignore order a_dict = ast.literal_eval(a) b_dict = ast.literal_eval(b) - if ( - isinstance(a_dict, dict) - and isinstance(b_dict, dict) - and a_dict == b_dict): + if isinstance(a_dict, dict) and isinstance(b_dict, dict) and a_dict == b_dict: return True except Exception: # Attribute values aren't dicts pass # on failure, try to split a and b at whitespace and compare snippets - def match_splits(a_, b_): # pragma: no cover + def match_splits(a_, b_): # pragma: no cover if len(a_) != len(b_): return False for a, b in zip(a_, b_): @@ -90,8 +87,8 @@ def match_splits(a_, b_): # pragma: no cover def all_attributes_match(a, b): - if len(a.attributes) != len(b.attributes): # pragma: no cover - print('Different number of attributes') + if len(a.attributes) != len(b.attributes): # pragma: no cover + print("Different number of attributes") return False a_atts = a.attributes.items() b_atts = b.attributes.items() @@ -99,11 +96,11 @@ def all_attributes_match(a, b): b_atts.sort() for a, b in zip(a_atts, b_atts): - if a[0] != b[0]: # pragma: no cover - print('Different attribute names: %s and %s' % (a[0], b[0])) + if a[0] != b[0]: # pragma: no cover + print("Different attribute names: %s and %s" % (a[0], b[0])) return False - if not text_values_match(a[1], b[1]): # pragma: no cover - print('Different attribute values: %s and %s' % (a[1], b[1])) + if not text_values_match(a[1], b[1]): # pragma: no cover + print("Different attribute values: %s and %s" % (a[1], b[1])) return False return True @@ -118,29 +115,31 @@ def text_matches(a, b): def nodes_match(a, b, ignore_nodes): if not a and not b: return True - if not a or not b: # pragma: no cover + if not a or not b: # pragma: no cover return False - if a.nodeType != b.nodeType: # pragma: no cover - print('Different node types: %s and %s' % (a, b)) + if a.nodeType != b.nodeType: # pragma: no cover + print("Different node types: %s and %s" % (a, b)) return False # compare text-valued nodes - if a.nodeType in [xml.dom.Node.TEXT_NODE, - xml.dom.Node.CDATA_SECTION_NODE, - xml.dom.Node.COMMENT_NODE]: + if a.nodeType in [ + xml.dom.Node.TEXT_NODE, + xml.dom.Node.CDATA_SECTION_NODE, + xml.dom.Node.COMMENT_NODE, + ]: return text_matches(a.data, b.data) # ignore all other nodes except ELEMENTs - if a.nodeType != xml.dom.Node.ELEMENT_NODE: # pragma: no cover + if a.nodeType != xml.dom.Node.ELEMENT_NODE: # pragma: no cover return True # compare ELEMENT nodes - if a.nodeName != b.nodeName: # pragma: no cover - print('Different element names: %s and %s' % (a.nodeName, b.nodeName)) + if a.nodeName != b.nodeName: # pragma: no cover + print("Different element names: %s and %s" % (a.nodeName, b.nodeName)) return False - if not all_attributes_match(a, b): # pragma: no cover + if not all_attributes_match(a, b): # pragma: no cover return False a = a.firstChild @@ -148,20 +147,24 @@ def nodes_match(a, b, ignore_nodes): while a or b: # ignore whitespace-only text nodes # we could have several text nodes in a row, due to replacements - while (a and - ((a.nodeType in ignore_nodes) or - ( - a.nodeType == xml.dom.Node.TEXT_NODE - and whitespace.sub('', a.data) == ""))): + while a and ( + (a.nodeType in ignore_nodes) + or ( + a.nodeType == xml.dom.Node.TEXT_NODE + and whitespace.sub("", a.data) == "" + ) + ): a = a.nextSibling - while (b and - ((b.nodeType in ignore_nodes) or - ( - b.nodeType == xml.dom.Node.TEXT_NODE - and whitespace.sub('', b.data) == ""))): + while b and ( + (b.nodeType in ignore_nodes) + or ( + b.nodeType == xml.dom.Node.TEXT_NODE + and whitespace.sub("", b.data) == "" + ) + ): b = b.nextSibling - if not nodes_match(a, b, ignore_nodes): # pragma: no cover + if not nodes_match(a, b, ignore_nodes): # pragma: no cover return False if a: @@ -182,11 +185,11 @@ def xml_matches(a, b, ignore_nodes=[]): if b.nodeType == xml.dom.Node.DOCUMENT_NODE: return xml_matches(a, b.documentElement, ignore_nodes) - if not nodes_match(a, b, ignore_nodes): # pragma: no cover - print('Match failed:') + if not nodes_match(a, b, ignore_nodes): # pragma: no cover + print("Match failed:") a.writexml(sys.stdout) print() - print('=' * 78) + print("=" * 78) b.writexml(sys.stdout) print() return False @@ -219,96 +222,100 @@ def test_mismatch_different_numbers(self): def test_match_unordered_dicts(self): self.assertTrue( - text_matches("{'a': 1, 'b': 2, 'c': 3}", "{'c': 3, 'b': 2, 'a': 1}")) + text_matches("{'a': 1, 'b': 2, 'c': 3}", "{'c': 3, 'b': 2, 'a': 1}") + ) def test_mismatch_different_dicts(self): self.assertFalse( - text_matches("{'a': 1, 'b': 2, 'c': 3}", "{'c': 3, 'b': 2, 'a': 0}")) + text_matches("{'a': 1, 'b': 2, 'c': 3}", "{'c': 3, 'b': 2, 'a': 0}") + ) def test_empty_node_vs_whitespace(self): - self.assertTrue(xml_matches('''''', ''' \t\n\r ''')) + self.assertTrue(xml_matches("""""", """ \t\n\r """)) def test_whitespace_vs_empty_node(self): - self.assertTrue(xml_matches(''' \t\n\r ''', '''''')) + self.assertTrue(xml_matches(""" \t\n\r """, """""")) def test_normalize_whitespace_nested(self): - self.assertTrue(xml_matches( - '''''', '''\n ''')) + self.assertTrue(xml_matches("""""", """\n """)) def test_ignore_comments(self): - self.assertTrue(xml_matches( - ''' ''', - '''''', [xml.dom.Node.COMMENT_NODE])) + self.assertTrue( + xml_matches( + """ """, + """""", + [xml.dom.Node.COMMENT_NODE], + ) + ) class TestXacroFunctions(unittest.TestCase): def test_is_valid_name(self): self.assertTrue(xacro.is_valid_name("_valid_name_123")) # syntactically correct keyword - self.assertFalse(xacro.is_valid_name('pass')) - self.assertFalse(xacro.is_valid_name('foo ')) # trailing whitespace - self.assertFalse(xacro.is_valid_name(' foo')) # leading whitespace - self.assertFalse(xacro.is_valid_name('1234')) # number - self.assertFalse(xacro.is_valid_name('1234abc')) # number and letters - self.assertFalse(xacro.is_valid_name('')) # empty string - self.assertFalse(xacro.is_valid_name(' ')) # whitespace only - self.assertFalse(xacro.is_valid_name('foo bar')) # several tokens - self.assertFalse(xacro.is_valid_name('no-dashed-names-for-you')) + self.assertFalse(xacro.is_valid_name("pass")) + self.assertFalse(xacro.is_valid_name("foo ")) # trailing whitespace + self.assertFalse(xacro.is_valid_name(" foo")) # leading whitespace + self.assertFalse(xacro.is_valid_name("1234")) # number + self.assertFalse(xacro.is_valid_name("1234abc")) # number and letters + self.assertFalse(xacro.is_valid_name("")) # empty string + self.assertFalse(xacro.is_valid_name(" ")) # whitespace only + self.assertFalse(xacro.is_valid_name("foo bar")) # several tokens + self.assertFalse(xacro.is_valid_name("no-dashed-names-for-you")) # dot separates fields - self.assertFalse(xacro.is_valid_name('invalid.too')) + self.assertFalse(xacro.is_valid_name("invalid.too")) def test_resolve_macro(self): # define three nested macro dicts with the same macro names (keys) - content = {'simple': 'simple'} - ns2 = dict({k: v + '2' for k, v in content.items()}) - ns1 = dict({k: v + '1' for k, v in content.items()}) + content = {"simple": "simple"} + ns2 = dict({k: v + "2" for k, v in content.items()}) + ns1 = dict({k: v + "1" for k, v in content.items()}) ns1.update(ns2=ns2) macros = dict(content) macros.update(ns1=ns1) - self.assertEqual(xacro.resolve_macro('simple', macros), 'simple') - self.assertEqual(xacro.resolve_macro('ns1.simple', macros), 'simple1') - self.assertEqual(xacro.resolve_macro( - 'ns1.ns2.simple', macros), 'simple2') + self.assertEqual(xacro.resolve_macro("simple", macros), "simple") + self.assertEqual(xacro.resolve_macro("ns1.simple", macros), "simple1") + self.assertEqual(xacro.resolve_macro("ns1.ns2.simple", macros), "simple2") def check_macro_arg(self, s, param, forward, default, rest): p, v, r = xacro.parse_macro_arg(s) - self.assertEqual( - p, param, msg="'{0}' != '{1}' parsing {2}".format(p, param, s)) + self.assertEqual(p, param, msg="'{0}' != '{1}' parsing {2}".format(p, param, s)) if forward or default: self.assertTrue(v is not None) self.assertEqual( - v[0], forward, msg="'{0}' != '{1}' parsing {2}".format( - v[0], forward, s)) + v[0], forward, msg="'{0}' != '{1}' parsing {2}".format(v[0], forward, s) + ) self.assertEqual( - v[1], default, msg="'{0}' != '{1}' parsing {2}".format( - v[1], default, s)) + v[1], default, msg="'{0}' != '{1}' parsing {2}".format(v[1], default, s) + ) else: self.assertTrue(v is None) - self.assertEqual( - r, rest, msg="'{0}' != '{1}' parsing {2}".format(r, rest, s)) + self.assertEqual(r, rest, msg="'{0}' != '{1}' parsing {2}".format(r, rest, s)) def test_parse_macro_arg(self): - for forward in ['', '^', '^|']: - defaults = ['', "f('some string','some other')", "f('a b')"] - if forward == '^': - defaults = [''] + for forward in ["", "^", "^|"]: + defaults = ["", "f('some string','some other')", "f('a b')"] + if forward == "^": + defaults = [""] for default in defaults: - seps = ['=', ':='] if forward or default else [''] + seps = ["=", ":="] if forward or default else [""] for sep in seps: - for rest in ['', ' ', ' bar', ' bar=42']: - s = 'foo{0}{1}{2}{3}'.format( - sep, forward, default, rest) + for rest in ["", " ", " bar", " bar=42"]: + s = "foo{0}{1}{2}{3}".format(sep, forward, default, rest) self.check_macro_arg( - s, 'foo', 'foo' if forward else None, - default if default else None, - rest.lstrip()) + s, + "foo", + "foo" if forward else None, + default if default else None, + rest.lstrip(), + ) def test_parse_macro_whitespace(self): - for ws in [' ', ' \t ', ' \n ']: + for ws in [" ", " \t ", " \n "]: self.check_macro_arg( - ws + 'foo' + ws + 'bar=42' + ws, - 'foo', None, None, 'bar=42' + ws) + ws + "foo" + ws + "bar=42" + ws, "foo", None, None, "bar=42" + ws + ) # base class providing some convenience functions @@ -331,9 +338,9 @@ def quick_xacro(self, xml, cli=None, **kwargs): xacro.process_doc(doc, **args) return doc - def run_xacro(self, input_path, *args): # pragma nocover + def run_xacro(self, input_path, *args): # pragma nocover args = list(args) - subprocess.call(['xacro', input_path] + args) + subprocess.call(["xacro", input_path] + args) # class to match XML docs while ignoring any comments @@ -346,13 +353,14 @@ def test_pr2(self): # run xacro on the pr2 tree snapshot test_dir = os.path.abspath(os.path.dirname(__file__)) print(test_dir) - pr2_xacro_path = os.path.join( - test_dir, 'robots', 'pr2', 'pr2.urdf.xacro') + pr2_xacro_path = os.path.join(test_dir, "robots", "pr2", "pr2.urdf.xacro") pr2_golden_parse_path = os.path.join( - test_dir, 'robots', 'pr2', 'pr2_1.11.4.xml') + test_dir, "robots", "pr2", "pr2_1.11.4.xml" + ) self.assert_matches( xml.dom.minidom.parse(pr2_golden_parse_path), - self.quick_xacro(open(pr2_xacro_path))) + self.quick_xacro(open(pr2_xacro_path)), + ) # standard test class (including the test from TestXacroCommentsIgnored) @@ -362,62 +370,65 @@ def __init__(self, *args, **kwargs): self.ignore_nodes = [] def test_invalid_property_name(self): - src = ''' - ''' + src = """ + """ self.assertRaises(xacro.XacroException, self.quick_xacro, src) def test_dynamic_macro_names(self): - src = ''' + src = """ foo bar - ''' - res = '''%s''' + """ + res = """%s""" self.assert_matches(self.quick_xacro(src % "foo"), res % "foo") self.assert_matches(self.quick_xacro(src % "bar"), res % "bar") def test_dynamic_macro_name_clash(self): - src = ''' + src = """ - ''' + """ self.assertRaises(xacro.XacroException, self.quick_xacro, src) def test_dynamic_macro_undefined(self): - self.assertRaises(xacro.XacroException, - self.quick_xacro, - ''' - ''') + self.assertRaises( + xacro.XacroException, + self.quick_xacro, + """ + """, + ) def test_macro_undefined(self): self.assertRaises( - xacro.XacroException, - self.quick_xacro, - ''' - ''') + xacro.XacroException, + self.quick_xacro, + """ + """, + ) def test_xacro_element(self): - src = ''' + src = """ -''' # noqa - res = '''''' +""" # noqa + res = """""" self.assert_matches(self.quick_xacro(src), res) def test_xacro_attribute(self): - src = ''' + src = """ -''' - res = '''''' +""" + res = """""" self.assert_matches(self.quick_xacro(src), res) def test_inorder_processing(self): - src = ''' + src = """ @@ -426,69 +437,69 @@ def test_inorder_processing(self): -''' - expected = ''' +""" + expected = """ -''' +""" self.assert_matches(self.quick_xacro(src), expected) def test_should_replace_before_macroexpand(self): - src = ''' + src = """ -''' # noqa - res = '''''' # noqa +""" # noqa + res = """""" # noqa self.assert_matches(self.quick_xacro(src), res) def test_evaluate_macro_params_before_body(self): - src = ''' + src = """ ${lst[-1]} - ''' - self.assert_matches(self.quick_xacro(src), '''3''') + """ + self.assert_matches(self.quick_xacro(src), """3""") def test_macro_params_escaped_string(self): - src = ''' + src = """ - ''' # noqa - self.assert_matches(self.quick_xacro(src), '''''') # noqa + """ # noqa + self.assert_matches(self.quick_xacro(src), """""") # noqa def test_property_replacement(self): - src = ''' + src = """ -''' - res = '''''' +""" + res = """""" self.assert_matches(self.quick_xacro(src), res) def test_property_scope_parent(self): - src = ''' + src = """ - ''' - self.assert_matches(self.quick_xacro(src), '''''') + """ + self.assert_matches(self.quick_xacro(src), """""") def test_property_scope_global(self): - src = ''' + src = """ - ''' - self.assert_matches(self.quick_xacro(src), '''''') + """ + self.assert_matches(self.quick_xacro(src), """""") def test_math_ignores_spaces(self): - src = '''''' - self.assert_matches(self.quick_xacro(src), '''''') + src = """""" + self.assert_matches(self.quick_xacro(src), """""") # def test_substitution_args_find(self): # self.assert_matches(self.quick_xacro(''''''), # noqa @@ -499,16 +510,17 @@ def test_math_ignores_spaces(self): # self.assert_matches(self.quick_xacro('''''', cli=['sub_arg:=my_arg']), res) # noqa def test_escaping_dollar_braces(self): - src = '''''' # noqa - res = '''''' # noqa + src = """""" # noqa + res = """""" # noqa self.assert_matches(self.quick_xacro(src), res) def test_just_a_dollar_sign(self): - src = '''''' + src = """""" self.assert_matches(self.quick_xacro(src), src) def test_multiple_insert_blocks(self): - self.assert_matches(self.quick_xacro(''' + self.assert_matches( + self.quick_xacro(""" @@ -516,13 +528,15 @@ def test_multiple_insert_blocks(self): -'''), ''' +"""), + """ -''') +""", + ) def test_multiple_blocks(self): - src = ''' + src = """ @@ -531,29 +545,32 @@ def test_multiple_blocks(self): -''' - res = ''' +""" + res = """ -''' +""" # test both, reversal and non-reversal of block order - for d in [dict(A='1', B='2'), dict(A='2', B='1')]: - self.assert_matches( - self.quick_xacro(src.format(**d)), res.format(**d)) + for d in [dict(A="1", B="2"), dict(A="2", B="1")]: + self.assert_matches(self.quick_xacro(src.format(**d)), res.format(**d)) def test_integer_stays_integer(self): - self.assert_matches(self.quick_xacro(''' + self.assert_matches( + self.quick_xacro(""" -'''), ''' +"""), + """ -''') +""", + ) def test_insert_block_property(self): - self.assert_matches(self.quick_xacro(''' + self.assert_matches( + self.quick_xacro(""" bar @@ -562,10 +579,12 @@ def test_insert_block_property(self): -'''), ''' +"""), + """ bar -''') +""", + ) # def test_include(self): # src = '''''' # noqa @@ -578,40 +597,44 @@ def test_insert_block_property(self): # self.assert_matches(self.quick_xacro(src.format(glob=pattern)), res) # noqa def test_include_nonexistent(self): - self.assertRaises(xacro.XacroException, - self.quick_xacro, ''' - ''') # noqa + self.assertRaises( + xacro.XacroException, + self.quick_xacro, + """ + """, + ) # noqa def test_include_deprecated(self): # tags with some non-trivial content should not issue the deprecation warning # noqa - src = '''''' + src = """""" with capture_stderr(self.quick_xacro, src) as (result, output): self.assert_matches(result, src) - self.assertEqual(output, '') + self.assertEqual(output, "") def test_include_from_variable(self): - doc = ''' + doc = """ - ''' # noqa - self.assert_matches(self.quick_xacro(doc), '''''') + """ # noqa + self.assert_matches(self.quick_xacro(doc), """""") def test_include_with_namespace(self): - src = ''' + src = """
-''' # noqa - res = ''' +""" # noqa + res = """
-''' +""" self.assert_matches(self.quick_xacro(src), res) def test_boolean_if_statement(self): - self.assert_matches(self.quick_xacro(''' + self.assert_matches( + self.quick_xacro(""" @@ -619,19 +642,24 @@ def test_boolean_if_statement(self): -'''), ''' +"""), + """ -''') +""", + ) def test_invalid_if_statement(self): - self.assertRaises(xacro.XacroException, - self.quick_xacro, - ''' - ''') + self.assertRaises( + xacro.XacroException, + self.quick_xacro, + """ + """, + ) def test_integer_if_statement(self): - self.assert_matches(self.quick_xacro(''' + self.assert_matches( + self.quick_xacro(""" @@ -645,13 +673,16 @@ def test_integer_if_statement(self): -'''), ''' +"""), + """ -''') +""", + ) def test_float_if_statement(self): - self.assert_matches(self.quick_xacro(''' + self.assert_matches( + self.quick_xacro(""" @@ -659,71 +690,91 @@ def test_float_if_statement(self): -'''), ''' +"""), + """ -''') +""", + ) def test_property_if_statement(self): - self.assert_matches(self.quick_xacro(''' + self.assert_matches( + self.quick_xacro(""" -'''), ''' +"""), + """ -''') +""", + ) def test_consecutive_if(self): - self.assert_matches(self.quick_xacro(''' + self.assert_matches( + self.quick_xacro(""" bar -'''), '''''') +"""), + """""", + ) def test_equality_expression_in_if_statement(self): - self.assert_matches(self.quick_xacro(''' + self.assert_matches( + self.quick_xacro(""" bar foo -'''), ''' +"""), + """ bar foo -''') +""", + ) def test_no_evaluation(self): - self.assert_matches(self.quick_xacro(''' + self.assert_matches( + self.quick_xacro(""" ${xyz} -'''), ''' +"""), + """ 5 -2 -''') +""", + ) def test_math_expressions(self): - self.assert_matches(self.quick_xacro(''' + self.assert_matches( + self.quick_xacro(""" -'''), ''' +"""), + """ -''') +""", + ) def test_consider_non_elements_if(self): - self.assert_matches(self.quick_xacro(''' + self.assert_matches( + self.quick_xacro(""" text bar -'''), ''' - text bar''') +"""), + """ + text bar""", + ) def test_consider_non_elements_block(self): self.assert_matches( - self.quick_xacro(''' + self.quick_xacro(""" foo @@ -734,15 +785,18 @@ def test_consider_non_elements_block(self): ignored text -'''), ''' +"""), + """ foo -''') +""", + ) def test_ignore_xacro_comments(self): - self.assert_matches(self.quick_xacro(''' + self.assert_matches( + self.quick_xacro(""" @@ -755,81 +809,102 @@ def test_ignore_xacro_comments(self): -'''), ''' -''') +"""), + """ +""", + ) def test_recursive_evaluation(self): - self.assert_matches(self.quick_xacro(''' + self.assert_matches( + self.quick_xacro(""" -'''), ''' +"""), + """ -''') +""", + ) def test_recursive_evaluation_wrong_order(self): - self.assert_matches(self.quick_xacro(''' + self.assert_matches( + self.quick_xacro(""" -'''), ''' +"""), + """ -''') +""", + ) def test_recursive_definition(self): - self.assertRaises(xacro.XacroException, - self.quick_xacro, ''' + self.assertRaises( + xacro.XacroException, + self.quick_xacro, + """ -''') +""", + ) def test_multiple_recursive_evaluation(self): - self.assert_matches(self.quick_xacro(''' + self.assert_matches( + self.quick_xacro(""" -'''), ''' +"""), + """ -''') +""", + ) def test_multiple_definition_and_evaluation(self): - self.assert_matches(self.quick_xacro(''' + self.assert_matches( + self.quick_xacro(""" -'''), ''' +"""), + """ -''') +""", + ) def test_transitive_evaluation(self): - self.assert_matches(self.quick_xacro(''' + self.assert_matches( + self.quick_xacro(""" -'''), ''' +"""), + """ -''') +""", + ) def test_multi_tree_evaluation(self): - self.assert_matches(self.quick_xacro(''' + self.assert_matches( + self.quick_xacro(""" @@ -837,35 +912,45 @@ def test_multi_tree_evaluation(self): -'''), ''' +"""), + """ -''') +""", + ) def test_from_issue(self): - self.assert_matches(self.quick_xacro(''' + self.assert_matches( + self.quick_xacro(""" -'''), ''' +"""), + """ -''') +""", + ) def test_recursive_bad_math(self): - self.assertRaises(xacro.XacroException, self.quick_xacro, ''' + self.assertRaises( + xacro.XacroException, + self.quick_xacro, + """ -''') +""", + ) def test_default_param(self): - self.assert_matches(self.quick_xacro(''' + self.assert_matches( + self.quick_xacro(""" @@ -878,7 +963,8 @@ def test_default_param(self): -'''), ''' +"""), + """ @@ -886,10 +972,12 @@ def test_default_param(self): -''') # noqa +""", + ) # noqa def test_default_param_override(self): - self.assert_matches(self.quick_xacro(''' + self.assert_matches( + self.quick_xacro(""" @@ -902,7 +990,8 @@ def test_default_param_override(self): -'''), ''' +"""), + """ @@ -910,10 +999,14 @@ def test_default_param_override(self): -''') # noqa +""", + ) # noqa def test_param_missing(self): - self.assertRaises(xacro.XacroException, self.quick_xacro, ''' + self.assertRaises( + xacro.XacroException, + self.quick_xacro, + """ @@ -926,164 +1019,185 @@ def test_param_missing(self): -''') +""", + ) def test_default_arg(self): - self.assert_matches(self.quick_xacro(''' + self.assert_matches( + self.quick_xacro(""" -'''), ''' +"""), + """ -''') - -# def test_default_arg_override(self): -# self.assert_matches(self.quick_xacro(''' -# -# -# -# -# -# -# ''', ['foo:=4']), ''' -# -# -# -# -# ''') - -# def test_default_arg_missing(self): -# self.assertRaises(Exception, self.quick_xacro, ''' -# -# -# -# ''') +""", + ) + + # def test_default_arg_override(self): + # self.assert_matches(self.quick_xacro(''' + # + # + # + # + # + # + # ''', ['foo:=4']), ''' + # + # + # + # + # ''') + + # def test_default_arg_missing(self): + # self.assertRaises(Exception, self.quick_xacro, ''' + # + # + # + # ''') def test_default_arg_empty(self): - self.assert_matches(self.quick_xacro(''' + self.assert_matches( + self.quick_xacro(""" -'''), '''''') +"""), + """""", + ) def test_iterable_literals_plain(self): - self.assert_matches(self.quick_xacro(''' + self.assert_matches( + self.quick_xacro(""" -'''), ''' +"""), + """ -''') +""", + ) def test_iterable_literals_eval(self): - self.assert_matches(self.quick_xacro(''' + self.assert_matches( + self.quick_xacro(""" -'''), ''' +"""), + """ -''') +""", + ) def test_literals_eval(self): - self.assert_matches(self.quick_xacro(''' + self.assert_matches( + self.quick_xacro(""" float=${f+1} int=${i+1} string=${s} -'''), ''' +"""), + """ float=2.23 int=124 string=1_2_3 -''') +""", + ) def test_enforce_xacro_ns(self): - self.assert_matches(self.quick_xacro(''' + self.assert_matches( + self.quick_xacro( + """ -''', xacro_ns=False), ''' +""", + xacro_ns=False, + ), + """ -''') - -# def test_issue_68_numeric_arg(self): -# # If a property is assigned from a substitution arg, then this properties' value was # noqa -# # no longer converted to a python type, so that e.g. 0.5 remained u'0.5'. # noqa -# # If this property is then used in a numerical expression an exception is thrown. # noqa -# self.assert_matches(self.quick_xacro(''' -# -# -# -# -# -# '''), ''' -# -# -# ''') - -# def test_transitive_arg_evaluation(self): -# self.assert_matches(self.quick_xacro(''' -# -# -# -# -# -# -# '''), ''' -# -# -# ''') +""", + ) + + # def test_issue_68_numeric_arg(self): + # # If a property is assigned from a substitution arg, then this properties' value was # noqa + # # no longer converted to a python type, so that e.g. 0.5 remained u'0.5'. # noqa + # # If this property is then used in a numerical expression an exception is thrown. # noqa + # self.assert_matches(self.quick_xacro(''' + # + # + # + # + # + # '''), ''' + # + # + # ''') + + # def test_transitive_arg_evaluation(self): + # self.assert_matches(self.quick_xacro(''' + # + # + # + # + # + # + # '''), ''' + # + # + # ''') def test_macro_name_with_colon(self): - src = ''' + src = """ - ''' - res = '''''' + """ + res = """""" with capture_stderr(self.quick_xacro, src) as (result, output): self.assert_matches(result, res) - self.assertTrue( - "macro names must not contain prefix 'xacro:'" in output) + self.assertTrue("macro names must not contain prefix 'xacro:'" in output) def test_overwrite_globals(self): - src = ''' - ''' + src = """ + """ with capture_stderr(self.quick_xacro, src) as (result, output): - self.assert_matches(result, '') + self.assert_matches(result, "") self.assertTrue(output) def test_no_double_evaluation(self): - src = ''' + src = """ a=${a} b=${b} c=${c} -''' # noqa - res = ''' a=2 b=1 c=${a} ''' +""" # noqa + res = """ a=2 b=1 c=${a} """ self.assert_matches(self.quick_xacro(src), res) def test_property_forwarding(self): - src = ''' + src = """ ${arg} - ''' - res = '''%s''' - self.assert_matches(self.quick_xacro(src % ''), res % '42') - self.assert_matches(self.quick_xacro(src % '|'), res % '42') - self.assert_matches(self.quick_xacro(src % '|6'), res % '42') + """ + res = """%s""" + self.assert_matches(self.quick_xacro(src % ""), res % "42") + self.assert_matches(self.quick_xacro(src % "|"), res % "42") + self.assert_matches(self.quick_xacro(src % "|6"), res % "42") # def test_extension_in_expression(self): # src = '''${2*'$(arg var)'}''' # noqa @@ -1096,13 +1210,13 @@ def test_property_forwarding(self): # self.assert_matches(self.quick_xacro(src, ['var:=xacro']), res % 'xacro') # noqa def test_target_namespace(self): - src = '''''' # noqa - res = '''''' + src = """""" # noqa + res = """""" self.assert_matches(self.quick_xacro(src), res) def test_target_namespace_only_from_root(self): - src = '''''' # noqa - res = '''''' + src = """""" # noqa + res = """""" self.assert_matches(self.quick_xacro(src), res) # def test_include_lazy(self): @@ -1110,14 +1224,14 @@ def test_target_namespace_only_from_root(self): # ''') # noqa # self.assert_matches(self.quick_xacro(doc), '''''') -# def test_issue_63_fixed_with_inorder_processing(self): -# self.assert_matches(self.quick_xacro(''' -# -# -# -# -# -# '''), '') + # def test_issue_63_fixed_with_inorder_processing(self): + # self.assert_matches(self.quick_xacro(''' + # + # + # + # + # + # '''), '') # def test_include_from_macro(self): # src = ''' @@ -1131,77 +1245,77 @@ def test_target_namespace_only_from_root(self): # res = '''''' # self.assert_matches(self.quick_xacro(src), res) -# def test_yaml_support(self): -# src = ''' -# -# -# -# -# -# ''' -# res = '''<{tag}/>''' -# for i in ['inc1', 'inc2']: -# self.assert_matches(self.quick_xacro(src, cli=['type:=%s' % i]), -# res.format(tag=i)) - -# def test_yaml_support_dotted(self): -# src = ''' -# -# -# -# -# -# ''' -# res = '''<{tag}/>''' -# for i in ['inc1', 'inc2']: -# self.assert_matches(self.quick_xacro(src, cli=['type:=%s' % i]), -# res.format(tag=i)) - -# def test_yaml_support_dotted_key_error(self): -# src = ''' -# -# -# -# ${bar} -# ''' -# self.assertRaises(xacro.XacroException, self.quick_xacro, src) - -# def test_yaml_support_dotted_arith(self): -# src = ''' -# -# -# -# ${bar} -# ''' -# res = '''4243''' -# self.assert_matches(self.quick_xacro(src), res) - -# def test_yaml_support_key_in_dict(self): -# src = ''' -# -# -# ${'arms' in settings} ${'baz' in settings} -# ''' -# res = '''True False''' -# self.assert_matches(self.quick_xacro(src), res) + # def test_yaml_support(self): + # src = ''' + # + # + # + # + # + # ''' + # res = '''<{tag}/>''' + # for i in ['inc1', 'inc2']: + # self.assert_matches(self.quick_xacro(src, cli=['type:=%s' % i]), + # res.format(tag=i)) + + # def test_yaml_support_dotted(self): + # src = ''' + # + # + # + # + # + # ''' + # res = '''<{tag}/>''' + # for i in ['inc1', 'inc2']: + # self.assert_matches(self.quick_xacro(src, cli=['type:=%s' % i]), + # res.format(tag=i)) + + # def test_yaml_support_dotted_key_error(self): + # src = ''' + # + # + # + # ${bar} + # ''' + # self.assertRaises(xacro.XacroException, self.quick_xacro, src) + + # def test_yaml_support_dotted_arith(self): + # src = ''' + # + # + # + # ${bar} + # ''' + # res = '''4243''' + # self.assert_matches(self.quick_xacro(src), res) + + # def test_yaml_support_key_in_dict(self): + # src = ''' + # + # + # ${'arms' in settings} ${'baz' in settings} + # ''' + # res = '''True False''' + # self.assert_matches(self.quick_xacro(src), res) def test_xacro_exist_required(self): - src = ''' + src = """ -''' +""" self.assertRaises(xacro.XacroException, self.quick_xacro, src) def test_xacro_exist_optional(self): - src = ''' + src = """ -''' - res = '''''' +""" + res = """""" self.assert_matches(self.quick_xacro(src), res) def test_macro_default_param_evaluation_order(self): - src = ''' + src = """ @@ -1210,13 +1324,13 @@ def test_macro_default_param_evaluation_order(self): -''' - res = ''' -''' +""" + res = """ +""" self.assert_matches(self.quick_xacro(src), res) def test_default_property(self): - src = ''' + src = """ @@ -1229,55 +1343,55 @@ def test_default_property(self): - ''' - res = '''''' + """ + res = """""" self.assert_matches(self.quick_xacro(src), res) def test_unicode_literal_parsing(self): - src = '''🍔 ''' - self.assert_matches(self.quick_xacro(src), '''🍔 ''') + src = """🍔 """ + self.assert_matches(self.quick_xacro(src), """🍔 """) def test_unicode_property(self): - src = ''' + src = """ -${burger}''' - res = '''🍔''' +${burger}""" + res = """🍔""" self.assert_matches(self.quick_xacro(src), res) def test_unicode_property_attribute(self): - src = ''' + src = """ -''' - res = '''''' +""" + res = """""" self.assert_matches(self.quick_xacro(src), res) def test_unicode_property_block(self): - src = ''' + src = """ 🍔 -''' - res = '''🍔''' +""" + res = """🍔""" self.assert_matches(self.quick_xacro(src), res) def test_unicode_conditional(self): - src = ''' + src = """ 🍟 -''' - res = '''🍟''' +""" + res = """🍟""" self.assert_matches(self.quick_xacro(src), res) def test_unicode_macro(self): - src = ''' + src = """ ${u'🍔' * how_many} -''' - res = '''🍔🍔🍔🍔''' +""" + res = """🍔🍔🍔🍔""" self.assert_matches(self.quick_xacro(src), res) # def test_unicode_file(self): @@ -1293,13 +1407,12 @@ def test_unicode_macro(self): # shutil.rmtree(tmp_dir_name) # clean up after ourselves def test_invalid_syntax(self): - self.assertRaises(xacro.XacroException, self.quick_xacro, 'a${') - self.assertRaises(xacro.XacroException, self.quick_xacro, '${b') - self.assertRaises( - xacro.XacroException, self.quick_xacro, '${{}}') - self.assertRaises(xacro.XacroException, self.quick_xacro, 'a$(') - self.assertRaises(xacro.XacroException, self.quick_xacro, '$(b') + self.assertRaises(xacro.XacroException, self.quick_xacro, "a${") + self.assertRaises(xacro.XacroException, self.quick_xacro, "${b") + self.assertRaises(xacro.XacroException, self.quick_xacro, "${{}}") + self.assertRaises(xacro.XacroException, self.quick_xacro, "a$(") + self.assertRaises(xacro.XacroException, self.quick_xacro, "$(b") -if __name__ == '__main__': # pragma nocover +if __name__ == "__main__": # pragma nocover unittest.main() diff --git a/roboticstoolbox/tools/xacro/xmlutils.py b/roboticstoolbox/tools/xacro/xmlutils.py index 6446e6856..4c7fa0938 100644 --- a/roboticstoolbox/tools/xacro/xmlutils.py +++ b/roboticstoolbox/tools/xacro/xmlutils.py @@ -101,16 +101,20 @@ def reqd_attrs(tag, attrs): :raise RuntimeError: if required attribute is missing """ result = opt_attrs(tag, attrs) - for (res, name) in zip(result, attrs): + for res, name in zip(result, attrs): if res is None: - raise RuntimeError("%s: missing attribute '%s'" % (tag.nodeName, name)) # pragma: no cover # noqa + raise RuntimeError( + "%s: missing attribute '%s'" % (tag.nodeName, name) + ) # pragma: no cover # noqa return result # Better pretty printing of xml # Taken from # http://ronrothman.com/public/leftbraned/xml-dom-minidom-toprettyxml-and-silly-whitespace/ # noqa -def fixed_writexml(self, writer, indent="", addindent="", newl=""): # pragma: no cover # noqa +def fixed_writexml( + self, writer, indent="", addindent="", newl="" +): # pragma: no cover # noqa # indent = current indentation # addindent = indentation to add to higher levels # newl = newline string @@ -120,12 +124,14 @@ def fixed_writexml(self, writer, indent="", addindent="", newl=""): # pragma: a_names = sorted(attrs.keys()) for a_name in a_names: - writer.write(" %s=\"" % a_name) + writer.write(' %s="' % a_name) xml.dom.minidom._write_data(writer, attrs[a_name].value) - writer.write("\"") + writer.write('"') if self.childNodes: - if len(self.childNodes) == 1 \ - and self.childNodes[0].nodeType == xml.dom.minidom.Node.TEXT_NODE: + if ( + len(self.childNodes) == 1 + and self.childNodes[0].nodeType == xml.dom.minidom.Node.TEXT_NODE + ): writer.write(">") self.childNodes[0].writexml(writer, "", "", "") writer.write("%s" % (self.tagName, newl)) @@ -133,8 +139,9 @@ def fixed_writexml(self, writer, indent="", addindent="", newl=""): # pragma: writer.write(">%s" % newl) for node in self.childNodes: # skip whitespace-only text nodes - if node.nodeType == xml.dom.minidom.Node.TEXT_NODE and \ - (not node.data or node.data.isspace()): + if node.nodeType == xml.dom.minidom.Node.TEXT_NODE and ( + not node.data or node.data.isspace() + ): continue node.writexml(writer, indent + addindent, addindent, newl) writer.write("%s%s" % (indent, self.tagName, newl)) diff --git a/rtb-data/setup.py b/rtb-data/setup.py index 6318a0fb2..f5a54f057 100644 --- a/rtb-data/setup.py +++ b/rtb-data/setup.py @@ -22,7 +22,7 @@ def package_files(directory): paths = [] - for (pathhere, _, filenames) in os.walk(directory): + for pathhere, _, filenames in os.walk(directory): for filename in filenames: paths.append(os.path.join("..", pathhere, filename)) return paths diff --git a/setup.py b/setup.py index 3de45fce7..41fdca08c 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ def package_files(directory): paths = [] - for (pathhere, _, filenames) in os.walk(directory): + for pathhere, _, filenames in os.walk(directory): for filename in filenames: paths.append(os.path.join("..", pathhere, filename)) return paths diff --git a/tests/examples.py b/tests/examples.py index dd6661635..330c72089 100644 --- a/tests/examples.py +++ b/tests/examples.py @@ -5,15 +5,14 @@ import unittest import sys -sys.path.append('./examples') +sys.path.append("./examples") -class TestExamples(unittest.TestCase): +class TestExamples(unittest.TestCase): def test_RRMC(self): - import RRMC - + pass -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/tests/test_BaseRobot.py b/tests/test_BaseRobot.py index 937b69dbd..c3d662ea1 100644 --- a/tests/test_BaseRobot.py +++ b/tests/test_BaseRobot.py @@ -17,7 +17,6 @@ class TestBaseRobot(unittest.TestCase): def test_init(self): - links, name, urdf_string, urdf_filepath = rtb.Robot.URDF_read( "franka_description/robots/panda_arm_hand.urdf.xacro" ) @@ -64,7 +63,6 @@ def test_init4(self): self.assertEqual(robot.comment, "other stuff") def test_init5(self): - base = SE3.Trans(0, 0, 0.1).A ets = ETS(rtb.ET.Rz()) robot = Robot(ets, base=base, tool=base) @@ -72,7 +70,6 @@ def test_init5(self): nt.assert_almost_equal(robot.tool.A, base) def test_init6(self): - base = SE3.Trans(0, 0, 0.1) ets = ETS(rtb.ET.Rz()) robot = Robot(ets, base=base, tool=base) @@ -80,7 +77,6 @@ def test_init6(self): nt.assert_almost_equal(robot.tool.A, base.A) def test_init7(self): - keywords = 2 ets = ETS(rtb.ET.Rz()) @@ -88,14 +84,12 @@ def test_init7(self): Robot(ets, keywords=keywords) # type: ignore def test_init8(self): - links = [2, 3, 4, 5] with self.assertRaises(TypeError): BaseRobot(links=links) # type: ignore def test_init9(self): - robot = rtb.models.Panda() robot2 = rtb.models.PR2() @@ -104,14 +98,12 @@ def test_init9(self): self.assertTrue(robot._hascollision) def test_init10(self): - links = [Link(name="link1"), Link(name="link1"), Link(name="link1")] with self.assertRaises(ValueError): Robot(links) def test_init11(self): - l1 = Link(parent="l3") l2 = Link(parent=l1) l3 = Link(parent=l2, name="l3") @@ -122,7 +114,6 @@ def test_init11(self): Robot(links) def test_init12(self): - l1 = Link(jindex=1, ets=rtb.ET.Rz()) l2 = Link(jindex=2, parent=l1, ets=rtb.ET.Rz()) l3 = Link(parent=l2, ets=rtb.ET.Rz()) @@ -143,7 +134,6 @@ def test_get(self): self.assertIsInstance(panda["link0"], Link) def test_init_ets(self): - ets = ( rtb.ET.tx(-0.0825) * rtb.ET.Rz() diff --git a/tests/test_CustomXacro.py b/tests/test_CustomXacro.py index 61aee8189..2c71ba0ff 100755 --- a/tests/test_CustomXacro.py +++ b/tests/test_CustomXacro.py @@ -18,7 +18,6 @@ class TestCustomXacro(unittest.TestCase): def test_custom(self): class CustomPanda(Robot): def __init__(self, xacro_path): - links, name, urdf_string, urdf_filepath = self.URDF_read( "franka_description/robots/panda_arm_hand.urdf.xacro", tld=xacro_path, @@ -63,5 +62,4 @@ def __init__(self, xacro_path): if __name__ == "__main__": - unittest.main() diff --git a/tests/test_ERobot.py b/tests/test_ERobot.py index be698bbb7..0a6ee9b49 100644 --- a/tests/test_ERobot.py +++ b/tests/test_ERobot.py @@ -197,7 +197,6 @@ def test_jacobe(self): @unittest.skipUnless(_sympy, "sympy not installed") def test_symdyn(self): - a1, a2, r1, r2, m1, m2, g = symbols("a1 a2 r1 r2 m1 m2 g") link1 = Link(ET.Ry(flip=True), m=m1, r=[r1, 0, 0], name="link0") link2 = Link(ET.tx(a1) * ET.Ry(flip=True), m=m2, r=[r2, 0, 0], name="link1") @@ -219,5 +218,4 @@ def test_symdyn(self): if __name__ == "__main__": # pragma nocover - unittest.main() diff --git a/tests/test_ET.py b/tests/test_ET.py index f820ad098..e2aa2677f 100644 --- a/tests/test_ET.py +++ b/tests/test_ET.py @@ -355,5 +355,4 @@ def test_et2_T(self): if __name__ == "__main__": - unittest.main() diff --git a/tests/test_ETS.py b/tests/test_ETS.py index 680bf4c69..791cee5b3 100644 --- a/tests/test_ETS.py +++ b/tests/test_ETS.py @@ -228,7 +228,8 @@ def test_fkine_sym(self): q2 = [y] ans6 = SE3.Rx(y) * tool nt.assert_almost_equal( - r2.fkine(q2, tool=tool).A, sympy.simplify(ans6.A) # type: ignore + r2.fkine(q2, tool=tool).A, + sympy.simplify(ans6.A), # type: ignore ) # nt.assert_almost_equal(r2.fkine(q2, tool=tool), ans6) # type: ignore diff --git a/tests/test_ETS2.py b/tests/test_ETS2.py index 7dea08126..09bde589f 100644 --- a/tests/test_ETS2.py +++ b/tests/test_ETS2.py @@ -1,4 +1,3 @@ - #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ @@ -462,5 +461,4 @@ def test_teach(self): if __name__ == "__main__": - unittest.main() diff --git a/tests/test_IK.py b/tests/test_IK.py index aacfe1029..333defb16 100644 --- a/tests/test_IK.py +++ b/tests/test_IK.py @@ -16,7 +16,6 @@ class TestIK(unittest.TestCase): def test_IK_NR1(self): - tol = 1e-6 panda = rtb.models.Panda().ets() @@ -36,7 +35,6 @@ def test_IK_NR1(self): self.assertGreater(test_tol, E) def test_IK_NR2(self): - q0 = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0] tol = 1e-6 @@ -58,7 +56,6 @@ def test_IK_NR2(self): self.assertGreater(test_tol, E) def test_IK_NR3(self): - q0 = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0] tol = 1e-6 @@ -80,7 +77,6 @@ def test_IK_NR3(self): self.assertGreater(test_tol, E) def test_IK_NR4(self): - q0 = np.array( [ [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0], @@ -110,7 +106,6 @@ def test_IK_NR4(self): self.assertGreater(test_tol, E) def test_IK_NR5(self): - tol = 1e-6 panda = rtb.models.Panda().ets() @@ -130,7 +125,6 @@ def test_IK_NR5(self): self.assertGreater(test_tol, E) def test_IK_NR6(self): - panda = rtb.models.Panda().ets() Tep = panda.eval([0, -0.3, 0, -2.2, 0, 2.0, np.pi / 4]) @@ -142,7 +136,6 @@ def test_IK_NR6(self): self.assertEqual(sol.success, False) def test_IK_NR7(self): - panda = rtb.models.Panda().ets() Tep = panda.eval([0, -1.3, 0, 1.2, 0, 2.0, 0.1]) @@ -155,7 +148,6 @@ def test_IK_NR7(self): @pytest.mark.filterwarnings("ignore::RuntimeWarning") def test_IK_NR8(self): - tol = 1e-6 panda = rtb.models.Panda().ets() @@ -183,7 +175,6 @@ def test_IK_NR8(self): @pytest.mark.filterwarnings("ignore::RuntimeWarning") def test_IK_LM1(self): - tol = 1e-6 panda = rtb.models.Panda().ets() @@ -205,7 +196,6 @@ def test_IK_LM1(self): self.assertGreater(test_tol, E) def test_IK_LM2(self): - tol = 1e-6 panda = rtb.models.Panda().ets() @@ -227,7 +217,6 @@ def test_IK_LM2(self): self.assertGreater(test_tol, E) def test_IK_LM3(self): - tol = 1e-6 panda = rtb.models.Panda().ets() @@ -250,7 +239,6 @@ def test_IK_LM3(self): @pytest.mark.filterwarnings("ignore::RuntimeWarning") def test_IK_GN1(self): - tol = 1e-6 panda = rtb.models.Panda().ets() @@ -272,7 +260,6 @@ def test_IK_GN1(self): self.assertGreater(test_tol, E) def test_IK_GN2(self): - tol = 1e-6 panda = rtb.models.Panda().ets() @@ -292,7 +279,6 @@ def test_IK_GN2(self): self.assertGreater(test_tol, E) def test_IK_GN3(self): - tol = 1e-6 ur5 = rtb.models.UR5().ets() @@ -312,7 +298,6 @@ def test_IK_GN3(self): self.assertGreater(test_tol, E) def test_IK_QP1(self): - q0 = np.array( [ -1.66441371, @@ -344,7 +329,6 @@ def test_IK_QP1(self): self.assertGreater(test_tol, E) def test_IK_QP2(self): - q0 = np.array( [ -1.66441371, @@ -376,7 +360,6 @@ def test_IK_QP2(self): self.assertGreater(test_tol, E) def test_IK_QP3(self): - tol = 1e-6 panda = rtb.models.Panda().ets() @@ -402,7 +385,6 @@ def test_IK_QP3(self): # self.assertEqual(sol.success, False) def test_ets_ikine_NR1(self): - tol = 1e-6 panda = rtb.models.Panda().ets() @@ -425,7 +407,6 @@ def test_ets_ikine_NR1(self): self.assertGreater(test_tol, E) def test_ets_ikine_NR2(self): - tol = 1e-6 panda = rtb.models.Panda().ets() @@ -445,7 +426,6 @@ def test_ets_ikine_NR2(self): self.assertGreater(test_tol, E) def test_ets_ikine_LM1(self): - tol = 1e-6 panda = rtb.models.Panda().ets() @@ -468,7 +448,6 @@ def test_ets_ikine_LM1(self): self.assertGreater(test_tol, E) def test_ets_ikine_LM2(self): - tol = 1e-6 panda = rtb.models.Panda().ets() @@ -488,7 +467,6 @@ def test_ets_ikine_LM2(self): self.assertGreater(test_tol, E) def test_ets_ikine_GN1(self): - tol = 1e-6 panda = rtb.models.Panda().ets() @@ -511,7 +489,6 @@ def test_ets_ikine_GN1(self): self.assertGreater(test_tol, E) def test_ets_ikine_GN2(self): - tol = 1e-6 panda = rtb.models.Panda().ets() @@ -531,7 +508,6 @@ def test_ets_ikine_GN2(self): self.assertGreater(test_tol, E) def test_ets_ikine_QP1(self): - q0 = np.array( [ -1.66441371, @@ -563,7 +539,6 @@ def test_ets_ikine_QP1(self): self.assertGreater(test_tol, E) def test_ets_ikine_QP2(self): - q0 = np.array( [ -1.66441371, @@ -598,7 +573,6 @@ def test_ets_ikine_QP2(self): self.assertGreater(test_tol, E) def test_ik_nr(self): - tol = 1e-6 solver = rtb.IK_LM() @@ -624,7 +598,6 @@ def test_ik_nr(self): self.assertGreater(test_tol, E2) def test_ik_lm_chan(self): - tol = 1e-6 solver = rtb.IK_LM() @@ -650,7 +623,6 @@ def test_ik_lm_chan(self): self.assertGreater(test_tol, E2) def test_ik_lm_wampler(self): - tol = 1e-6 solver = rtb.IK_LM() @@ -676,7 +648,6 @@ def test_ik_lm_wampler(self): self.assertGreater(test_tol, E2) def test_ik_lm_sugihara(self): - tol = 1e-6 solver = rtb.IK_LM() @@ -702,7 +673,6 @@ def test_ik_lm_sugihara(self): self.assertGreater(test_tol, E2) def test_ik_gn(self): - tol = 1e-6 solver = rtb.IK_LM() @@ -732,7 +702,6 @@ def test_ik_gn(self): self.assertGreater(test_tol, E2) def test_sol_print1(self): - sol = rtb.IKSolution( q=np.zeros(3), success=True, @@ -752,7 +721,6 @@ def test_sol_print1(self): self.assertEqual(s, ans) def test_sol_print2(self): - sol = rtb.IKSolution( q=None, # type: ignore success=True, @@ -769,7 +737,6 @@ def test_sol_print2(self): self.assertEqual(s, ans) def test_sol_print3(self): - sol = rtb.IKSolution( q=np.zeros(3), success=False, @@ -786,7 +753,6 @@ def test_sol_print3(self): self.assertEqual(s, ans) def test_sol_print4(self): - sol = rtb.IKSolution( q=np.zeros(3), success=True, @@ -803,7 +769,6 @@ def test_sol_print4(self): self.assertEqual(s, ans) def test_sol_print5(self): - sol = rtb.IKSolution( q=np.zeros(3), success=False, @@ -842,5 +807,4 @@ def test_iter_iksol(self): if __name__ == "__main__": - unittest.main() diff --git a/tests/test_Link.py b/tests/test_Link.py index 546ed9d50..8c8f207ca 100644 --- a/tests/test_Link.py +++ b/tests/test_Link.py @@ -170,7 +170,6 @@ def test_robot(self): self.assertIs(l0._robot, r) def test_copy(self): - l0 = rp.RevoluteMDH() r = rp.DHRobot([l0]) l1 = l0.copy() @@ -204,5 +203,4 @@ def test_qlim_none(self): if __name__ == "__main__": - unittest.main() diff --git a/tests/test_PyPlot.py b/tests/test_PyPlot.py index 1d6ce4f26..96a1eadf4 100644 --- a/tests/test_PyPlot.py +++ b/tests/test_PyPlot.py @@ -46,5 +46,4 @@ def test_unimplemented(self): if __name__ == "__main__": - unittest.main() diff --git a/tests/test_PyPlot2.py b/tests/test_PyPlot2.py index 66f25b1c2..6944791ff 100644 --- a/tests/test_PyPlot2.py +++ b/tests/test_PyPlot2.py @@ -6,15 +6,16 @@ # import numpy.testing as nt # import numpy as np import roboticstoolbox as rp + # import spatialmath as sm import unittest class TestPyPlot(unittest.TestCase): - def test_PyPlot(self): panda = rp.models.DH.Panda() from roboticstoolbox.backends.PyPlot import PyPlot2 + env = PyPlot2() env.launch() env.add(panda) @@ -25,6 +26,7 @@ def test_PyPlot(self): def test_PyPlot_invisible(self): panda = rp.models.DH.Panda() from roboticstoolbox.backends.PyPlot import PyPlot2 + env = PyPlot2() env.launch() env.add(panda, display=False) @@ -35,11 +37,13 @@ def test_PyPlot_invisible(self): def test_unimplemented(self): # TODO remove these as implemented from roboticstoolbox.backends.PyPlot import PyPlot2 + env = PyPlot2() env.reset() env.restart() env.remove() -if __name__ == '__main__': - unittest.main() \ No newline at end of file + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_Ticker.py b/tests/test_Ticker.py index c091a2d0c..1bcba896c 100644 --- a/tests/test_Ticker.py +++ b/tests/test_Ticker.py @@ -8,9 +8,8 @@ class TestTicker(unittest.TestCase): - def test_ticker(self): - self.skipTest('Not working on windows or mac') + self.skipTest("Not working on windows or mac") t = Ticker(0.1) diff --git a/tests/test_blocks.py b/tests/test_blocks.py index 357867d24..9176c0ef3 100644 --- a/tests/test_blocks.py +++ b/tests/test_blocks.py @@ -5,7 +5,9 @@ try: from bdsim import BDSim except ModuleNotFoundError: - raise unittest.SkipTest("bdsim not found, skipping all tests in test_blocks.py") from None + raise unittest.SkipTest( + "bdsim not found, skipping all tests in test_blocks.py" + ) from None from spatialmath import SE3 from spatialmath.base import tr2x @@ -16,6 +18,7 @@ from roboticstoolbox.blocks import * from roboticstoolbox.blocks.quad_model import quadrotor + class State: T = 5 @@ -27,9 +30,9 @@ def __init__(self): def __init__(self): self.options = self.Opt() + class RobotBlockTest(unittest.TestCase): def test_fkine(self): - robot = rtb.models.ETS.Panda() q = robot.configs["qr"] T = robot.fkine(q) @@ -38,7 +41,6 @@ def test_fkine(self): nt.assert_array_almost_equal(block.T_output(q)[0], T) def test_ikine(self): - robot = rtb.models.ETS.Panda() q = robot.configs["qr"] T = robot.fkine(q) @@ -51,7 +53,6 @@ def test_ikine(self): nt.assert_array_almost_equal(robot.fkine(q_ik), T) # test it's FK is correct def test_jacobian(self): - robot = rtb.models.ETS.Panda() q = robot.configs["qr"] @@ -68,7 +69,6 @@ def test_jacobian(self): nt.assert_array_almost_equal(block.T_output(q)[0], np.linalg.pinv(J)) def test_gravload(self): - robot = rtb.models.DH.Puma560() q = robot.configs["qn"] @@ -147,7 +147,6 @@ def test_fdyn_x(self): @unittest.skip("cant test bdsim plot blocks") def test_armplot(self): - robot = rtb.models.ETS.Panda() q = robot.configs["qr"] @@ -156,7 +155,6 @@ def test_armplot(self): class SpatialBlockTest(unittest.TestCase): def test_delta(self): - block = Tr2Delta() T1 = SE3() @@ -169,7 +167,6 @@ def test_delta(self): nt.assert_array_almost_equal(block.T_output(delta)[0], SE3.Delta(delta)) def test_tr2t(self): - T = SE3.Trans(1, 2, 3) * SE3.RPY(0.3, 0.4, 0.5) block = TR2T() @@ -180,7 +177,6 @@ def test_tr2t(self): self.assertAlmostEqual(out[2], 3) def test_point2tr(self): - T = SE3.Trans(1, 2, 3) * SE3.RPY(0.3, 0.4, 0.5) block = Point2Tr(T) @@ -203,7 +199,6 @@ def test_jtraj(self): nt.assert_array_almost_equal(block.T_output(t=5)[0], q2) def test_ctraj(self): - T1 = SE3.Trans(1, 2, 3) * SE3.RPY(0.3, 0.4, 0.5) T2 = SE3.Trans(-1, -2, -3) * SE3.RPY(-0.3, -0.4, -0.5) @@ -216,7 +211,6 @@ def test_ctraj(self): nt.assert_array_almost_equal(block.T_output(t=5)[0], T2) def test_trapezoidal(self): - block = Trapezoidal(2, 3, T=5) s = State() @@ -231,7 +225,6 @@ def test_trapezoidal(self): nt.assert_array_almost_equal(out[1], 0) def test_circlepath(self): - block = CirclePath( radius=2, centre=[1, 2, 3], frequency=0.25, phase=0, unit="rps" ) @@ -241,7 +234,6 @@ def test_circlepath(self): nt.assert_array_almost_equal(block.T_output(t=2)[0], (1 - 2, 2, 3)) def test_traj(self): - block = Traj([1, 2], [3, 4], time=True, traj="trapezoidal", T=5) s = State() block.start(s) @@ -270,7 +262,6 @@ def test_traj(self): class MobileBlockTest(unittest.TestCase): def test_bicycle(self): - x = [2, 3, np.pi / 2] block = Bicycle(x0=x, L=3) @@ -279,11 +270,11 @@ def test_bicycle(self): nt.assert_array_almost_equal(block.T_output(10, 0.3, x=x, t=0)[0], x) nt.assert_array_almost_equal( - block.T_deriv(10, 0.3, x=x), [10 * np.cos(x[2]), 10 * np.sin(x[2]), 10 / 3 * np.tan(0.3)] + block.T_deriv(10, 0.3, x=x), + [10 * np.cos(x[2]), 10 * np.sin(x[2]), 10 / 3 * np.tan(0.3)], ) def test_unicycle(self): - x = [2, 3, np.pi / 2] block = Unicycle(x0=x, W=3) @@ -296,7 +287,6 @@ def test_unicycle(self): ) def test_diffsteer(self): - x = [2, 3, np.pi / 2] block = DiffSteer(x0=x, W=3, R=1 / np.pi) @@ -308,7 +298,6 @@ def test_diffsteer(self): @unittest.skip("cant test bdsim plot blocks") def test_vehicleplot(self): - bike = Bicycle() block = VehiclePlot() @@ -319,7 +308,6 @@ def test_vehicleplot(self): class MultirotorBlockTest(unittest.TestCase): def test_multirotor(self): - x = np.r_[[1, 2, 3, 0, 0, 0], np.zeros((6,))] block = MultiRotor(model=quadrotor) @@ -328,12 +316,11 @@ def test_multirotor(self): )[0] self.assertIsInstance(out, dict) - out = block.T_deriv(100*np.r_[1, 1, 1, 1], x=x) + out = block.T_deriv(100 * np.r_[1, 1, 1, 1], x=x) self.assertIsInstance(out, np.ndarray) self.assertEqual(out.shape, (12,)) def test_multirotormixer(self): - block = MultiRotorMixer(model=quadrotor) nt.assert_array_almost_equal( block.T_output(0, 0, 0, -20, t=0)[0], @@ -342,7 +329,6 @@ def test_multirotormixer(self): @unittest.skip("cant test bdsim plot blocks") def test_multirotorplot(self): - block = MultiRotorPlot(model=quadrotor) class State: @@ -354,7 +340,6 @@ class State: block.step(state=s) def test_quadrotor(self): - block = MultiRotor(quadrotor) print(block.D) z = np.r_[0, 0, 0, 0] @@ -400,7 +385,6 @@ def test_quadrotor(self): @unittest.skip("cant test bdsim plot blocks") def test_quadrotorplot(self): - block = MultiRotor(quadrotor) u = [100 * np.r_[1, -1, 1, -1]] x = block.getstate0() @@ -413,5 +397,4 @@ def test_quadrotorplot(self): # ---------------------------------------------------------------------------------------# if __name__ == "__main__": - unittest.main() diff --git a/tests/test_mobile.py b/tests/test_mobile.py index 7143f6bd8..d25619658 100644 --- a/tests/test_mobile.py +++ b/tests/test_mobile.py @@ -6,11 +6,9 @@ import numpy.testing as nt import roboticstoolbox as rtb import numpy as np -import spatialmath.base as sm import unittest # from roboticstoolbox import Bug2, DistanceTransformPlanner, rtb_loadmat -from roboticstoolbox import Bug2 from roboticstoolbox.mobile.Bug2 import edgelist from roboticstoolbox.mobile.landmarkmap import * from roboticstoolbox.mobile.drivers import * @@ -82,14 +80,12 @@ def setUp(self): self.rs = RangeBearingSensor(self.veh, self.map) def test_init(self): - self.assertIsInstance(self.rs.map, rtb.LandmarkMap) self.assertIsInstance(self.rs.robot, rtb.Bicycle) self.assertIsInstance(str(self.rs), str) def test_reading(self): - z, lm_id = self.rs.reading() self.assertIsInstance(z, np.ndarray) self.assertEqual(z.shape, (2,)) @@ -205,7 +201,6 @@ def test_G_jacobians(self): ) def test_plot(self): - # map = LandmarkMap(20) # map.plot(block=False) pass @@ -216,7 +211,6 @@ def test_plot(self): class LandMarkTest(unittest.TestCase): def test_init(self): - map = LandmarkMap(20) self.assertEqual(len(map), 20) @@ -247,7 +241,6 @@ def test_plot(self): class DriversTest(unittest.TestCase): def test_init(self): - rp = rtb.RandomPath(10) self.assertIsInstance(str(rp), str) @@ -267,7 +260,6 @@ def test_init(self): class TestBicycle(unittest.TestCase): - # def test_deriv(self): # xv = np.r_[1, 2, pi/4] diff --git a/tests/test_mobile_planner.py b/tests/test_mobile_planner.py index 67ad4f8d2..0d4b543a7 100644 --- a/tests/test_mobile_planner.py +++ b/tests/test_mobile_planner.py @@ -1,26 +1,25 @@ -from collections import namedtuple from math import pi -import numpy.testing as nt import numpy as np import unittest -import spatialmath.base as sm from roboticstoolbox.mobile import * class TestPlanners(unittest.TestCase): - def test_occgrid(self): g = np.zeros((100, 120)) g[20:30, 50:80] = 1 - og = BinaryOccupancyGrid(g, name='my grid') + og = BinaryOccupancyGrid(g, name="my grid") self.assertEqual(og.shape, g.shape) - + s = str(og) self.assertIsInstance(s, str) - self.assertEqual(s, "BinaryOccupancyGrid[my grid]: 120 x 100, cell size=1, x = [0.0, 119.0], y = [0.0, 99.0], 2.5% occupied") + self.assertEqual( + s, + "BinaryOccupancyGrid[my grid]: 120 x 100, cell size=1, x = [0.0, 119.0], y = [0.0, 99.0], 2.5% occupied", + ) self.assertEqual(og.xmin, 0) self.assertEqual(og.xmax, 119) @@ -58,12 +57,15 @@ def test_occgrid(self): self.assertTrue(og2.isoccupied((80, 30))) self.assertTrue(og2.isoccupied((45, 20))) - self.assertEqual(str(og2), "BinaryOccupancyGrid[my grid]: 120 x 100, cell size=1, x = [0.0, 119.0], y = [0.0, 99.0], 6.3% occupied") + self.assertEqual( + str(og2), + "BinaryOccupancyGrid[my grid]: 120 x 100, cell size=1, x = [0.0, 119.0], y = [0.0, 99.0], 6.3% occupied", + ) # check no change to original self.assertFalse(og.isoccupied((80, 30))) - og = BinaryOccupancyGrid(g, cellsize=0.1, origin=(2,4), name='foo') + og = BinaryOccupancyGrid(g, cellsize=0.1, origin=(2, 4), name="foo") self.assertEqual(og.xmin, 2) self.assertEqual(og.xmax, 13.9) @@ -74,39 +76,40 @@ def test_occgrid(self): self.assertFalse(og.isoccupied((3, 5))) og.inflate(0.5) - self.assertEqual(str(og), "BinaryOccupancyGrid[foo]: 120 x 100, cell size=0.1, x = [2.0, 13.9], y = [4.0, 13.9], 6.3% occupied") + self.assertEqual( + str(og), + "BinaryOccupancyGrid[foo]: 120 x 100, cell size=0.1, x = [2.0, 13.9], y = [4.0, 13.9], 6.3% occupied", + ) def test_bug2(self): pass def test_dubins(self): - - start = (0, 0, pi/2) - goal = (1, 0, pi/2) + start = (0, 0, pi / 2) + goal = (1, 0, pi / 2) dubins = DubinsPlanner(curvature=1.0) path, status = dubins.query(start, goal) self.assertIsInstance(path, np.ndarray) - self.assertEqual(path.shape, (74,3)) + self.assertEqual(path.shape, (74, 3)) self.assertEqual(status.__class__.__name__, "DubinsStatus") - self.assertTrue(hasattr(status, 'segments')) - self.assertTrue(hasattr(status, 'length')) + self.assertTrue(hasattr(status, "segments")) + self.assertTrue(hasattr(status, "length")) def test_reedsshepp(self): - - start = (0, 0, pi/2) - goal = (1, 0, pi/2) + start = (0, 0, pi / 2) + goal = (1, 0, pi / 2) rs = ReedsSheppPlanner(curvature=1.0) path, status = rs.query(start, goal) self.assertIsInstance(path, np.ndarray) - self.assertEqual(path.shape, (65,3)) + self.assertEqual(path.shape, (65, 3)) self.assertEqual(status.__class__.__name__, "ReedsSheppStatus") - self.assertTrue(hasattr(status, 'segments')) - self.assertTrue(hasattr(status, 'length')) - self.assertTrue(hasattr(status, 'direction')) + self.assertTrue(hasattr(status, "segments")) + self.assertTrue(hasattr(status, "length")) + self.assertTrue(hasattr(status, "direction")) # def test_bug2(self): @@ -168,10 +171,11 @@ def test_reedsshepp(self): # for k in range(len(path)-1): # d = np.linalg.norm(path[k] - path[k+1]) # self.assertTrue(d < 1.5) - + # dx.plot() # dx.plot(path=path) -if __name__ == '__main__': # pragma nocover + +if __name__ == "__main__": # pragma nocover unittest.main() # pytest.main(['tests/test_SerialLink.py']) diff --git a/tests/test_models.py b/tests/test_models.py index 466937c08..735f450d9 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -5,7 +5,6 @@ import roboticstoolbox as rp import unittest -import numpy.testing as nt class TestModels(unittest.TestCase): diff --git a/tests/test_tools.py b/tests/test_tools.py index 6d7d6d39b..e891b4110 100644 --- a/tests/test_tools.py +++ b/tests/test_tools.py @@ -12,7 +12,6 @@ class Testtools(unittest.TestCase): def test_null(self): - a0 = np.array([1, 2, 3]) a1 = np.array([[1, 2, 3], [4, 5, 6]]) diff --git a/tests/test_trajectory.py b/tests/test_trajectory.py index 15fbb1e27..594b50ae2 100644 --- a/tests/test_trajectory.py +++ b/tests/test_trajectory.py @@ -3,6 +3,7 @@ """ @author: Peter Corke """ + import roboticstoolbox.tools.trajectory as tr from roboticstoolbox import xplot import numpy as np @@ -15,9 +16,7 @@ class TestTrajectory(unittest.TestCase): - def test_quintic(self): - s1 = 1 s2 = 2 # no boundary conditions @@ -81,20 +80,18 @@ def test_quintic(self): self.assertAlmostEqual(sdd[-1], 0) with self.assertRaises(TypeError): - tr.quintic(s1, s2, 'not time') + tr.quintic(s1, s2, "not time") def test_quintic_plot(self): t = tr.quintic(0, 1, 50) t.plot() - t = tr.quintic(0, 1, np.linspace(0,1,50)) + t = tr.quintic(0, 1, np.linspace(0, 1, 50)) t.plot() - def test_trapezoidal(self): - - s1 = 1. - s2 = 2. + s1 = 1.0 + s2 = 2.0 # no boundary conditions @@ -145,7 +142,7 @@ def test_trapezoidal(self): self.assertAlmostEqual(sd[5], 0.2) with self.assertRaises(TypeError): - tr.trapezoidal(s1, s2, 'not time') + tr.trapezoidal(s1, s2, "not time") with self.assertRaises(ValueError): tr.trapezoidal(s1, s2, t, V=0.000000001) @@ -156,11 +153,10 @@ def test_trapezoidal(self): def test_trapezoidal_plot(self): t = tr.trapezoidal(0, 1, 50) t.plot() - t = tr.trapezoidal(0, 1, np.linspace(0,1,50)) + t = tr.trapezoidal(0, 1, np.linspace(0, 1, 50)) t.plot() def test_plot(self): - # 6 joints is special q1 = np.r_[1, 2, 3, 4, 5, 6] q2 = -q1 @@ -192,8 +188,8 @@ def test_ctraj(self): nt.assert_array_almost_equal(T[1].A, T0.A) nt.assert_array_almost_equal(T[2].A, SE3().A) - T0 = SE3.Rx(-pi/2) - T1 = SE3.Rx(pi/2) + T0 = SE3.Rx(-pi / 2) + T1 = SE3.Rx(pi / 2) T = tr.ctraj(T0, T1, 3) self.assertEqual(len(T), 3) @@ -202,7 +198,7 @@ def test_ctraj(self): nt.assert_array_almost_equal(T[1].A, SE3().A) with self.assertRaises(TypeError): - tr.ctraj(T0, T1, 'hello') + tr.ctraj(T0, T1, "hello") def test_cmstraj(self): tr.cmstraj() @@ -220,16 +216,58 @@ def test_mtraj(self): self.assertAlmostEqual(q.shape, (11, 6)) self.assertTrue(np.allclose(q[0, :], q1)) self.assertTrue(np.allclose(q[-1, :], q2)) - self.assertTrue(np.allclose(q[5, :], np.zeros(6,))) + self.assertTrue( + np.allclose( + q[5, :], + np.zeros( + 6, + ), + ) + ) self.assertAlmostEqual(qd.shape, (11, 6)) - self.assertTrue(np.allclose(qd[0, :], np.zeros(6,))) - self.assertTrue(np.allclose(qd[-1, :], np.zeros(6,))) + self.assertTrue( + np.allclose( + qd[0, :], + np.zeros( + 6, + ), + ) + ) + self.assertTrue( + np.allclose( + qd[-1, :], + np.zeros( + 6, + ), + ) + ) self.assertAlmostEqual(qdd.shape, (11, 6)) - self.assertTrue(np.allclose(qdd[0, :], np.zeros(6,))) - self.assertTrue(np.allclose(qdd[-1, :], np.zeros(6,))) - self.assertTrue(np.allclose(qdd[5, :], np.zeros(6,))) + self.assertTrue( + np.allclose( + qdd[0, :], + np.zeros( + 6, + ), + ) + ) + self.assertTrue( + np.allclose( + qdd[-1, :], + np.zeros( + 6, + ), + ) + ) + self.assertTrue( + np.allclose( + qdd[5, :], + np.zeros( + 6, + ), + ) + ) # with a time vector t = np.linspace(0, 2, 11) @@ -242,16 +280,58 @@ def test_mtraj(self): self.assertAlmostEqual(q.shape, (11, 6)) self.assertTrue(np.allclose(q[0, :], q1)) self.assertTrue(np.allclose(q[-1, :], q2)) - self.assertTrue(np.allclose(q[5, :], np.zeros(6,))) + self.assertTrue( + np.allclose( + q[5, :], + np.zeros( + 6, + ), + ) + ) self.assertAlmostEqual(qd.shape, (11, 6)) - self.assertTrue(np.allclose(qd[0, :], np.zeros(6,))) - self.assertTrue(np.allclose(qd[-1, :], np.zeros(6,))) + self.assertTrue( + np.allclose( + qd[0, :], + np.zeros( + 6, + ), + ) + ) + self.assertTrue( + np.allclose( + qd[-1, :], + np.zeros( + 6, + ), + ) + ) self.assertAlmostEqual(qdd.shape, (11, 6)) - self.assertTrue(np.allclose(qdd[0, :], np.zeros(6,))) - self.assertTrue(np.allclose(qdd[-1, :], np.zeros(6,))) - self.assertTrue(np.allclose(qdd[5, :], np.zeros(6,))) + self.assertTrue( + np.allclose( + qdd[0, :], + np.zeros( + 6, + ), + ) + ) + self.assertTrue( + np.allclose( + qdd[-1, :], + np.zeros( + 6, + ), + ) + ) + self.assertTrue( + np.allclose( + qdd[5, :], + np.zeros( + 6, + ), + ) + ) # unit testing jtraj with trapezoidal q1 = np.r_[1, 2, 3, 4, 5, 6] @@ -265,11 +345,32 @@ def test_mtraj(self): self.assertAlmostEqual(q.shape, (11, 6)) self.assertTrue(np.allclose(q[0, :], q1)) self.assertTrue(np.allclose(q[-1, :], q2)) - self.assertTrue(np.allclose(q[5, :], np.zeros(6,))) + self.assertTrue( + np.allclose( + q[5, :], + np.zeros( + 6, + ), + ) + ) self.assertAlmostEqual(qd.shape, (11, 6)) - self.assertTrue(np.allclose(qd[0, :], np.zeros(6,))) - self.assertTrue(np.allclose(qd[-1, :], np.zeros(6,))) + self.assertTrue( + np.allclose( + qd[0, :], + np.zeros( + 6, + ), + ) + ) + self.assertTrue( + np.allclose( + qd[-1, :], + np.zeros( + 6, + ), + ) + ) self.assertAlmostEqual(qdd.shape, (11, 6)) @@ -284,11 +385,32 @@ def test_mtraj(self): self.assertAlmostEqual(q.shape, (11, 6)) self.assertTrue(np.allclose(q[0, :], q1)) self.assertTrue(np.allclose(q[-1, :], q2)) - self.assertTrue(np.allclose(q[5, :], np.zeros(6,))) + self.assertTrue( + np.allclose( + q[5, :], + np.zeros( + 6, + ), + ) + ) self.assertAlmostEqual(qd.shape, (11, 6)) - self.assertTrue(np.allclose(qd[0, :], np.zeros(6,))) - self.assertTrue(np.allclose(qd[-1, :], np.zeros(6,))) + self.assertTrue( + np.allclose( + qd[0, :], + np.zeros( + 6, + ), + ) + ) + self.assertTrue( + np.allclose( + qd[-1, :], + np.zeros( + 6, + ), + ) + ) self.assertAlmostEqual(qdd.shape, (11, 6)) @@ -305,16 +427,58 @@ def test_jtraj(self): self.assertAlmostEqual(q.shape, (11, 6)) self.assertTrue(np.allclose(q[0, :], q1)) self.assertTrue(np.allclose(q[-1, :], q2)) - self.assertTrue(np.allclose(q[5, :], np.zeros(6,))) + self.assertTrue( + np.allclose( + q[5, :], + np.zeros( + 6, + ), + ) + ) self.assertAlmostEqual(qd.shape, (11, 6)) - self.assertTrue(np.allclose(qd[0, :], np.zeros(6,))) - self.assertTrue(np.allclose(qd[-1, :], np.zeros(6,))) + self.assertTrue( + np.allclose( + qd[0, :], + np.zeros( + 6, + ), + ) + ) + self.assertTrue( + np.allclose( + qd[-1, :], + np.zeros( + 6, + ), + ) + ) self.assertAlmostEqual(qdd.shape, (11, 6)) - self.assertTrue(np.allclose(qdd[0, :], np.zeros(6,))) - self.assertTrue(np.allclose(qdd[-1, :], np.zeros(6,))) - self.assertTrue(np.allclose(qdd[5, :], np.zeros(6,))) + self.assertTrue( + np.allclose( + qdd[0, :], + np.zeros( + 6, + ), + ) + ) + self.assertTrue( + np.allclose( + qdd[-1, :], + np.zeros( + 6, + ), + ) + ) + self.assertTrue( + np.allclose( + qdd[5, :], + np.zeros( + 6, + ), + ) + ) # with a time vector t = np.linspace(0, 2, 11) @@ -327,16 +491,58 @@ def test_jtraj(self): self.assertAlmostEqual(q.shape, (11, 6)) self.assertTrue(np.allclose(q[0, :], q1)) self.assertTrue(np.allclose(q[-1, :], q2)) - self.assertTrue(np.allclose(q[5, :], np.zeros(6,))) + self.assertTrue( + np.allclose( + q[5, :], + np.zeros( + 6, + ), + ) + ) self.assertAlmostEqual(qd.shape, (11, 6)) - self.assertTrue(np.allclose(qd[0, :], np.zeros(6,))) - self.assertTrue(np.allclose(qd[-1, :], np.zeros(6,))) + self.assertTrue( + np.allclose( + qd[0, :], + np.zeros( + 6, + ), + ) + ) + self.assertTrue( + np.allclose( + qd[-1, :], + np.zeros( + 6, + ), + ) + ) self.assertAlmostEqual(qdd.shape, (11, 6)) - self.assertTrue(np.allclose(qdd[0, :], np.zeros(6,))) - self.assertTrue(np.allclose(qdd[-1, :], np.zeros(6,))) - self.assertTrue(np.allclose(qdd[5, :], np.zeros(6,))) + self.assertTrue( + np.allclose( + qdd[0, :], + np.zeros( + 6, + ), + ) + ) + self.assertTrue( + np.allclose( + qdd[-1, :], + np.zeros( + 6, + ), + ) + ) + self.assertTrue( + np.allclose( + qdd[5, :], + np.zeros( + 6, + ), + ) + ) # test with boundary conditions qone = np.ones((6,)) @@ -354,8 +560,22 @@ def test_jtraj(self): self.assertTrue(np.allclose(qd[-1, :], qone)) self.assertAlmostEqual(qdd.shape, (11, 6)) - self.assertTrue(np.allclose(qdd[0, :], np.zeros(6,))) - self.assertTrue(np.allclose(qdd[-1, :], np.zeros(6,))) + self.assertTrue( + np.allclose( + qdd[0, :], + np.zeros( + 6, + ), + ) + ) + self.assertTrue( + np.allclose( + qdd[-1, :], + np.zeros( + 6, + ), + ) + ) with self.assertRaises(ValueError): tr.jtraj(q1, [1, 1, 2], t) @@ -367,52 +587,50 @@ def test_jtraj(self): tr.jtraj(q1, q2, t, qd0=[1, 1]) def test_mstraj(self): - - via = np.array([ - [4, 1], - [4, 4], - [5, 2], - [2, 5] - ]) + via = np.array([[4, 1], [4, 4], [5, 2], [2, 5]]) # Test with QDMAX out = tr.mstraj(via, dt=1, tacc=1, qdmax=[2, 1], q0=[4, 1]) # expected_out = mstraj(via, [ 2 1 ],[],[4 1],1,1,1); - expected_out = np.array([ - [4.0000, 1.0000], - [4.0000, 1.7500], - [4.0000, 2.5000], - [4.0000, 3.2500], - [4.3333, 3.3333], - [4.6667, 2.6667], - [4.2500, 2.7500], - [3.5000, 3.5000], - [2.7500, 4.2500], - [2.0000, 5.0000] - ]) + expected_out = np.array( + [ + [4.0000, 1.0000], + [4.0000, 1.7500], + [4.0000, 2.5000], + [4.0000, 3.2500], + [4.3333, 3.3333], + [4.6667, 2.6667], + [4.2500, 2.7500], + [3.5000, 3.5000], + [2.7500, 4.2500], + [2.0000, 5.0000], + ] + ) nt.assert_array_almost_equal(out.q, expected_out, decimal=4) # Test with QO # expected_out = mstraj(via, [], [2 1 3 4],[4 1],1,1,1); out = tr.mstraj(via, dt=1, tacc=1, tsegment=[2, 1, 3, 4], q0=[4, 1]) - expected_out = np.array([ - [4.0000, 1.0000], - [4.0000, 4.0000], - [4.3333, 3.3333], - [4.6667, 2.6667], - [4.2500, 2.7500], - [3.5000, 3.5000], - [2.7500, 4.2500], - [2.0000, 5.0000] - ]) + expected_out = np.array( + [ + [4.0000, 1.0000], + [4.0000, 4.0000], + [4.3333, 3.3333], + [4.6667, 2.6667], + [4.2500, 2.7500], + [3.5000, 3.5000], + [2.7500, 4.2500], + [2.0000, 5.0000], + ] + ) nt.assert_array_almost_equal(out.q, expected_out, decimal=4) out = tr.mstraj(via, dt=1, tacc=1, tsegment=[1, 2, 3, 4], q0=via[0, :]) self.assertEqual(out.t.shape[0], out.q.shape[0]) self.assertIsInstance(out.info, list) - self.assertEqual(len(out.info), via.shape[0]+1) + self.assertEqual(len(out.info), via.shape[0] + 1) tr.mstraj(via, dt=1, tacc=1, qdmax=[2, 1]) tr.mstraj(via, dt=1, tacc=1, qdmax=2) @@ -436,16 +654,13 @@ def test_mstraj(self): tr.mstraj(via, dt=1, tacc=[1, 2, 3, 4, 5], qdmax=[2, 1]) with self.assertRaises(ValueError): - tr.mstraj( - via, dt=1, tacc=1, qdmax=[2, 1], qd0=[1, 2, 3], q0=[1, 2]) + tr.mstraj(via, dt=1, tacc=1, qdmax=[2, 1], qd0=[1, 2, 3], q0=[1, 2]) with self.assertRaises(ValueError): - tr.mstraj( - via, dt=1, tacc=1, qdmax=[2, 1], qdf=[1, 2, 3], q0=[1, 2]) - + tr.mstraj(via, dt=1, tacc=1, qdmax=[2, 1], qdf=[1, 2, 3], q0=[1, 2]) -if __name__ == '__main__': # pragma nocover +if __name__ == "__main__": # pragma nocover unittest.main() # function mtraj_quintic_test(tc) From 292f17a41a639f2ddeffa40177cca62cfc50a59e Mon Sep 17 00:00:00 2001 From: Robertleoj Date: Thu, 19 Dec 2024 15:17:39 +0100 Subject: [PATCH 2/2] fix some ruff errors --- roboticstoolbox/robot/DHFactor.py | 11 +- roboticstoolbox/robot/Dynamics.py | 7 +- roboticstoolbox/tools/DHFactor.py | 7 +- roboticstoolbox/tools/data.py | 10 +- roboticstoolbox/tools/trajectory.py | 25 +-- tests/test_DHRobot.py | 9 - tests/test_ETS2.py | 13 -- tests/test_blocks.py | 27 +-- tests/test_mobile.py | 304 +--------------------------- tests/test_mobile_planner.py | 2 +- tests/test_trajectory.py | 5 - 11 files changed, 33 insertions(+), 387 deletions(-) diff --git a/roboticstoolbox/robot/DHFactor.py b/roboticstoolbox/robot/DHFactor.py index b517f54ed..8af885704 100644 --- a/roboticstoolbox/robot/DHFactor.py +++ b/roboticstoolbox/robot/DHFactor.py @@ -4,8 +4,8 @@ import numpy as np from spatialmath import base -pi2 = base.pi() / 2 -deg = base.pi() / sympy.Integer("180") +pi2 = base.symbolic.pi() / 2 +deg = base.symbolic.pi() / sympy.Integer("180") # PROGRESS @@ -26,7 +26,6 @@ def parse(cls, s): for axis, eta in et_re.findall(s): if eta[0] == "q": eta = None - unit = None j = jointnum jointnum += 1 else: @@ -34,7 +33,7 @@ def parse(cls, s): try: # first attempt to create symbolic number eta = sympy.Number(eta) - except: + except Exception: # failing that, a symbolic variable eta = sympy.symbols(eta) if axis[0] == "R": @@ -129,10 +128,6 @@ def float_right(self): # ---------------------------------------------------------------------- # def swap(self, mdh=False): # we want to sort terms into the order: - # RZ - # TX - # TZ - # RX def do_swap(this, next): if mdh: diff --git a/roboticstoolbox/robot/Dynamics.py b/roboticstoolbox/robot/Dynamics.py index 0c607ece2..49205ba10 100644 --- a/roboticstoolbox/robot/Dynamics.py +++ b/roboticstoolbox/robot/Dynamics.py @@ -18,7 +18,6 @@ from scipy import integrate, interpolate from roboticstoolbox import rtb_get_param from roboticstoolbox.robot.RobotProto import RobotProto - from roboticstoolbox.tools.types import ArrayLike, NDArray import roboticstoolbox as rtb @@ -1536,15 +1535,11 @@ def paycap( verifymatrix(tauR, (self.n, 2)) wmax = np.zeros((trajn, 6)) - joint = np.zeros(trajn, dtype=np.int) + joint = np.zeros(trajn, dtype=np.int64) for i in range(trajn): tauB = self.gravload(q[i, :]) - # tauP = self.rne( - # np.zeros(self.n), np.zeros(self.n), - # q, grav=[0, 0, 0], fext=w/np.linalg.norm(w)) - tauP = self.pay(w[i, :] / np.linalg.norm(w[i, :]), q=q[i, :], frame=frame) M = tauP > 0 diff --git a/roboticstoolbox/tools/DHFactor.py b/roboticstoolbox/tools/DHFactor.py index 2302a7086..8a52a9211 100644 --- a/roboticstoolbox/tools/DHFactor.py +++ b/roboticstoolbox/tools/DHFactor.py @@ -1,6 +1,3 @@ -""" -@author Samuel Drew -""" class Element: # pragma nocover @@ -208,7 +205,7 @@ def factorMatch(self, dhWhich, i, verbose): return match def merge(self, e): - assert type(e) == Element, "merge(Element e)" + assert isinstance(e, Element), "merge(Element e)" """ don't merge if dissimilar transform or both are joint variables @@ -230,7 +227,7 @@ def merge(self, e): return sum def swap(self, next, dhWhich): - assert type(next) == Element, "type(next) == Element" + assert isinstance(next, Element), "type(next) == Element" # don't swap if both are joint variables if self.isjoint() and next.isjoint(): diff --git a/roboticstoolbox/tools/data.py b/roboticstoolbox/tools/data.py index c272a7cd5..06150a407 100644 --- a/roboticstoolbox/tools/data.py +++ b/roboticstoolbox/tools/data.py @@ -1,5 +1,8 @@ from pathlib import Path import importlib +from scipy.io import loadmat +from scipy.io.matlab.mio5_params import mat_struct + def rtb_load_matfile(filename): @@ -25,9 +28,6 @@ def rtb_load_matfile(filename): :seealso: :func:`path_to_datafile` """ - from scipy.io import loadmat - from scipy.io.matlab.mio5_params import mat_struct - # get results as a dict data = rtb_load_data(filename, loadmat, squeeze_me=True, struct_as_record=False) @@ -157,7 +157,7 @@ def rtb_path_to_datafile(*filename, local=True): if __name__ == "__main__": house = rtb_load_matfile("data/house.mat") - a = rtb_loadmat("map1.mat") + a = rtb_load_matfile("map1.mat") print(a) - a = rtb_loadmat("data/map1.mat") + a = rtb_load_matfile("data/map1.mat") print(a) diff --git a/roboticstoolbox/tools/trajectory.py b/roboticstoolbox/tools/trajectory.py index b12e5cda9..1ed814dab 100644 --- a/roboticstoolbox/tools/trajectory.py +++ b/roboticstoolbox/tools/trajectory.py @@ -2,12 +2,14 @@ import math import warnings from collections import namedtuple +from spatialmath import SO2 import matplotlib.pyplot as plt from spatialmath.base.argcheck import ( isvector, getvector, isscalar, ) +import roboticstoolbox as rtb class Trajectory: @@ -260,7 +262,7 @@ def qplot(self, **kwargs): :seealso: :func:`qplot` """ - xplot(self.t, self.q, **kwargs) + rtb.xplot(self.t, self.q, **kwargs) # -------------------------------------------------------------------------- # @@ -1124,26 +1126,7 @@ def mrange(start, stop, step): if __name__ == "__main__": - # t = quintic(0, 1, 50) - # t.plot() - - # t = quintic(0, 1, np.linspace(0, 1, 50)) - # t.plot() - - # t = trapezoidal(0, 1, 50) - # t.plot() - # t = trapezoidal(0, 1, np.linspace(0, 1, 50)) - # t.plot(block=True) - - from roboticstoolbox import * - from spatialmath import SO2 - - # puma = models.DH.Puma560() - - # traj = jtraj(puma.qz, puma.qr, 100) - # traj.plot(block=True) - via = SO2(30, unit="deg") * np.array([[-1, 1, 1, -1, -1], [1, 1, -1, -1, 1]]) traj0 = mstraj(via.T, dt=0.2, tacc=0.5, qdmax=[2, 1]) - xplot(traj0.q[:, 0], traj0.q[:, 1], color="red") + rtb.xplot(traj0.q[:, 0], traj0.q[:, 1], color="red") traj0.plot(block=True) diff --git a/tests/test_DHRobot.py b/tests/test_DHRobot.py index 8144f00ec..256948376 100644 --- a/tests/test_DHRobot.py +++ b/tests/test_DHRobot.py @@ -1,8 +1,3 @@ -#!/usr/bin/env python3 -""" -@author: Jesse Haviland -""" - import numpy.testing as nt import numpy as np import roboticstoolbox as rp @@ -61,8 +56,6 @@ def test_isrevolute(self): r0 = rp.DHRobot([l0, l1, l2, l3]) - ans = [False, True, False, True] - self.assertEqual(r0.isrevolute(0), False) self.assertEqual(r0.isrevolute(1), True) self.assertEqual(r0.isrevolute(2), False) @@ -1173,8 +1166,6 @@ def test_gravload(self): puma = rp.models.DH.Puma560() q = puma.qn - grav = [0, 0, 9.81] - taur = [-0.0000, 31.6399, 6.0351, 0.0000, 0.0283, 0] tau0 = puma.gravload(q) diff --git a/tests/test_ETS2.py b/tests/test_ETS2.py index 09bde589f..1006222ce 100644 --- a/tests/test_ETS2.py +++ b/tests/test_ETS2.py @@ -1,10 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Fri May 1 14:04:04 2020 -@author: Jesse Haviland -""" - import numpy.testing as nt import roboticstoolbox as rtb import numpy as np @@ -28,8 +21,6 @@ def test_bad_arg(self): def test_args(self): deg = np.pi / 180 - mm = 1e-3 - tool_offset = (103) * mm l0 = rtb.ET2.tx(0.333) * rtb.ET2.R(jindex=0) @@ -55,7 +46,6 @@ def test_args(self): r5 = rtb.ETS2( [l0, l1, l2, l3, l3, l4, rtb.ET2.R(90 * deg), rtb.ET2.R(jindex=5)] ) - r6 = rtb.ETS2([r1]) r7 = rtb.ETS2(rtb.ET2.R(1.0)) self.assertEqual(r1, r2) @@ -137,7 +127,6 @@ def ets_mul(self): def test_n(self): rx = rtb.ET2.R(1.543) ry = rtb.ET2.R(1.543) - rz = rtb.ET2.R(1.543) a = rtb.ET2.R() b = rtb.ET2.R() @@ -262,7 +251,6 @@ def test_inv(self): nt.assert_almost_equal(r_inv.fkine(q), ans1.inv().A) def test_jointset(self): - q = [1.0, 2.0, 3.0] a = rtb.ET2.R(jindex=0) b = rtb.ET2.R(jindex=1) c = rtb.ET2.R(jindex=2) @@ -450,7 +438,6 @@ def test_plot(self): r.plot(q=q2, block=False) def test_teach(self): - x = sympy.Symbol("x") q2 = np.array([0, 1, 2]) rz = rtb.ETS2(rtb.ET2.R(jindex=0)) tx = rtb.ETS2(rtb.ET2.tx(jindex=1, qlim=[-1, 1])) diff --git a/tests/test_blocks.py b/tests/test_blocks.py index 9176c0ef3..4788a2df2 100644 --- a/tests/test_blocks.py +++ b/tests/test_blocks.py @@ -1,21 +1,12 @@ -#!/usr/bin/env python3 - import unittest - -try: - from bdsim import BDSim -except ModuleNotFoundError: - raise unittest.SkipTest( - "bdsim not found, skipping all tests in test_blocks.py" - ) from None - +import numpy as np from spatialmath import SE3 from spatialmath.base import tr2x import numpy.testing as nt import roboticstoolbox as rtb -from roboticstoolbox.blocks import * +from roboticstoolbox.blocks import FKine, IKine, Jacobian, Gravload, Gravload_X, Inertia, Inertia_X, IDyn, FDyn, FDyn_X, MultiRotor, MultiRotorPlot, MultiRotorMixer, VehiclePlot, Bicycle, DiffSteer, Unicycle, Traj, CirclePath, Trapezoidal, CTraj, JTraj, Point2Tr, TR2T, Delta2Tr, Tr2Delta, ArmPlot from roboticstoolbox.blocks.quad_model import quadrotor @@ -44,7 +35,7 @@ def test_ikine(self): robot = rtb.models.ETS.Panda() q = robot.configs["qr"] T = robot.fkine(q) - sol = robot.ikine_LM(T) + robot.ikine_LM(T) block = IKine(robot, seed=0) @@ -148,9 +139,8 @@ def test_fdyn_x(self): @unittest.skip("cant test bdsim plot blocks") def test_armplot(self): robot = rtb.models.ETS.Panda() - q = robot.configs["qr"] - - block = ArmPlot(robot) + robot.configs["qr"] + ArmPlot(robot) class SpatialBlockTest(unittest.TestCase): @@ -298,7 +288,6 @@ def test_diffsteer(self): @unittest.skip("cant test bdsim plot blocks") def test_vehicleplot(self): - bike = Bicycle() block = VehiclePlot() s = State() @@ -388,11 +377,7 @@ def test_quadrotorplot(self): block = MultiRotor(quadrotor) u = [100 * np.r_[1, -1, 1, -1]] x = block.getstate0() - out = block.T_output(u, x=x)[0] - - # block = MultiRotorPlot(quadrotor) - # s = block.T_start() - # block.T_step(out, s) + block.T_output(u, x=x)[0] # ---------------------------------------------------------------------------------------# diff --git a/tests/test_mobile.py b/tests/test_mobile.py index d25619658..d6a9b40d1 100644 --- a/tests/test_mobile.py +++ b/tests/test_mobile.py @@ -6,14 +6,14 @@ import numpy.testing as nt import roboticstoolbox as rtb import numpy as np +import matplotlib.pyplot as plt import unittest -# from roboticstoolbox import Bug2, DistanceTransformPlanner, rtb_loadmat from roboticstoolbox.mobile.Bug2 import edgelist -from roboticstoolbox.mobile.landmarkmap import * -from roboticstoolbox.mobile.drivers import * -from roboticstoolbox.mobile.sensors import * -from roboticstoolbox.mobile.Vehicle import * +from roboticstoolbox.mobile.landmarkmap import LandmarkMap +from roboticstoolbox.mobile.drivers import base +from roboticstoolbox.mobile.sensors import RangeBearingSensor +from roboticstoolbox.mobile.Vehicle import Unicycle, Bicycle # from roboticstoolbox.mobile import Planner @@ -49,26 +49,6 @@ def test_edgelist(self): for e in edge: self.assertEqual(im[e[1], e[0]], im[seed[1], seed[0]]) - # def test_map(self): - # map = np.zeros((10, 10)) - # map[2, 3] = 1 - - # # instantiate a noname planner - # nav = Planner(occgrid=map, ndims=2) - - # ## test isoccupied method - # self.assertTrue(nav.isoccupied([3, 2])) - # self.assertFalse(nav.isoccupied([3, 3])) - - # # out of bounds - # self.assertTrue(nav.isoccupied([20, 20])) - - # ## test inflation option - # nav = Bug2(occgrid=map, inflate=1) - # self.assertTrue(nav.isoccupied([3, 2])) - # self.assertTrue(nav.isoccupied([3, 3])) - # self.assertFalse(nav.isoccupied([3, 4])) - # ======================================================================== # @@ -146,16 +126,8 @@ def test_h(self): for i in range(3): nt.assert_almost_equal(z[i, :], self.rs.h(xv[i, :], 10)) - # xv = np.r_[2, 3, 0.5] - # p = np.array([[1, 2], [3, 4], [5, 6]]).T - # z = self.rs.h(xv, p) - # self.assertIsInstance(z, np.ndarray) - # self.assertEqual(z.shape, (3,2)) - # for i in range(3): - # nt.assert_almost_equal(z[i,:], self.rs.h(xv, p[i,:])) - def test_H_jacobians(self): - xv = np.r_[1, 2, pi / 4] + xv = np.r_[1, 2, np.pi / 4] p = np.r_[5, 7] id = 10 @@ -167,7 +139,7 @@ def test_H_jacobians(self): self.rs.Hp(xv, p), base.numjac(lambda p: self.rs.h(xv, p), p), decimal=4 ) - xv = [1, 2, pi / 4] + xv = [1, 2, np.pi / 4] p = [5, 7] id = 10 @@ -180,14 +152,14 @@ def test_H_jacobians(self): ) def test_g(self): - xv = np.r_[1, 2, pi / 4] + xv = np.r_[1, 2, np.pi / 4] p = np.r_[5, 7] z = self.rs.h(xv, p) nt.assert_almost_equal(p, self.rs.g(xv, z)) def test_G_jacobians(self): - xv = np.r_[1, 2, pi / 4] + xv = np.r_[1, 2, np.pi / 4] p = np.r_[5, 7] z = self.rs.h(xv, p) @@ -260,19 +232,8 @@ def test_init(self): class TestBicycle(unittest.TestCase): - # def test_deriv(self): - # xv = np.r_[1, 2, pi/4] - - # veh = Bicycle() - - # u = [1, 0.2] - # nt.assert_almost_equal( - # veh.deriv(xv, ), - # base.numjac(lambda p: veh(xv, p), p), - # decimal=4) - def test_jacobians(self): - xv = np.r_[1, 2, pi / 4] + xv = np.r_[1, 2, np.pi / 4] odo = np.r_[0.1, 0.2] veh = Bicycle() @@ -321,248 +282,5 @@ def test_deriv(self): nt.assert_almost_equal(uni.deriv(state, input), np.r_[1, 0, 1]) -# function setupOnce(testCase) -# testCase.TestData.Duration = 50; -# end - -# function Vehicle_test(tc) -# %% -# randinit -# V = diag([0.005, 0.5*pi/180].^2); - -# v = Bicycle('covar', V); -# v.add_driver( RandomPath(10) ); - -# v.run(tc.TestData.Duration); -# v.plot_xy(); -# s = v.char(); - -# J = v.Fx(v.x, [.1 .2]); -# J = v.Fv(v.x, [.1 .2]); -# end - -# function DeadReckoning_test(tc) -# %% -# randinit -# V = diag([0.005, 0.5*pi/180].^2); -# P0 = diag([0.005, 0.005, 0.001].^2); - -# v = Bicycle('covar', V); -# v.add_driver( RandomPath(10) ); -# s = char(v); - -# ekf = EKF(v, V, P0); -# ekf.run(tc.TestData.Duration); - -# clf -# ekf.plot_xy -# hold on -# v.plot_xy('r') -# grid on -# xyzlabel - -# ekf.plot_ellipse('g') -# ekf.plot_P() -# end - -# function MapLocalization_test(tc) -# %% -# randinit -# W = diag([0.1, 1*pi/180].^2); -# P0 = diag([0.005, 0.005, 0.001].^2); -# V = diag([0.005, 0.5*pi/180].^2); - -# map = LandmarkMap(20); -# map = LandmarkMap(20, 'verbose'); -# map = LandmarkMap(20, 10, 'verbose'); -# map = LandmarkMap(20, 10); -# s = char(map); - -# veh = Bicycle('covar', V); -# veh.add_driver( RandomPath(10) ); -# sensor = RangeBearingSensor(veh, map, 'covar', W); -# sensor.interval = 5; -# ekf = EKF(veh, W, P0, sensor, W, map); - -# ekf.run(tc.TestData.Duration); - -# clf -# map.plot() -# veh.plot_xy('b'); -# ekf.plot_xy('r'); -# ekf.plot_ellipse('k') -# grid on -# xyzlabel - -# clf -# ekf.plot_P() -# end - -# function Mapping_test(tc) -# %% -# randinit -# W = diag([0.1, 1*pi/180].^2); -# V = diag([0.005, 0.5*pi/180].^2); - -# map = LandmarkMap(20, 10); - -# veh = Bicycle('covar', V); -# veh.add_driver( RandomPath(10) ); - -# sensor = RangeBearingSensor(veh, map, 'covar', W); -# sensor.interval = 5; - -# ekf = EKF(veh, [], [], sensor, W, []); -# ekf.run(tc.TestData.Duration); - - -# clf -# map.plot() -# veh.plot_xy('b'); -# ekf.plot_map('g'); -# grid on -# xyzlabel - -# %% -# verifyEqual(tc, numcols(ekf.landmarks), 20); - -# end - -# function SLAM_test(tc) -# %% -# randinit -# W = diag([0.1, 1*pi/180].^2); -# P0 = diag([0.005, 0.005, 0.001].^2); -# V = diag([0.005, 0.5*pi/180].^2); - -# map = LandmarkMap(20, 10); - -# veh = Bicycle(V); -# veh.add_driver( RandomPath(10) ); - -# sensor = RangeBearingSensor(veh, map, 'covar', W); -# sensor.interval = 1; - -# ekf = EKF(veh, V, P0, sensor, W, []); -# ekf -# ekf.verbose = false; -# ekf.run(tc.TestData.Duration); - - -# clf -# map.plot() -# veh.plot_xy('b'); -# ekf.plot_xy('r'); -# ekf.plot_ellipse('k') -# grid on -# xyzlabel - -# clf -# ekf.plot_P() - -# clf -# map.plot(); -# ekf.plot_map('g'); - -# %% -# verifyEqual(tc, numcols(ekf.landmarks), 20); - -# end - -# function ParticleFilter_test(tc) -# %% -# randinit -# map = LandmarkMap(20); - -# W = diag([0.1, 1*pi/180].^2); -# v = Bicycle('covar', W); -# v.add_driver( RandomPath(10) ); -# V = diag([0.005, 0.5*pi/180].^2); -# sensor = RangeBearingSensor(v, map, 'covar', V); - -# Q = diag([0.1, 0.1, 1*pi/180]).^2; -# L = diag([0.1 0.1]); -# pf = ParticleFilter(v, sensor, Q, L, 1000); -# pf -# pf.run(tc.TestData.Duration); - -# plot(pf.std) -# xlabel('time step') -# ylabel('standard deviation') -# legend('x', 'y', '\theta') -# grid - -# clf -# pf.plot_pdf(); -# clf -# pf.plot_xy(); -# end - -# function posegraph_test(tc) -# pg = PoseGraph('pg1.g2o') -# self.assertClass(pg, 'PoseGraph'); -# self.assertEqual(pg.graph.n, 4); - -# clf -# pg.plot() -# pg.optimize('animate') -# close all - -# pg = PoseGraph('killian-small.toro') -# self.assertClass(pg, 'PoseGraph'); -# self.assertEqual(pg.graph.n, 1941); - -# pg = PoseGraph('killian.g2o', 'laser') -# self.assertClass(pg, 'PoseGraph'); -# self.assertEqual(pg.graph.n, 3873); - -# [r,theta] = pg.scan(1); -# self.assertClass(r, 'double'); -# self.assertLength(r, 180); -# self.assertClass(theta, 'double'); -# self.assertLength(theta, 180); - -# [x,y] = pg.scan(1); -# self.assertClass(x, 'double'); -# self.assertLength(x, 180); -# self.assertClass(y, 'double'); -# self.assertLength(y, 180); - -# pose = pg.pose(1); -# self.assertClass(pose, 'double'); -# self.assertSize(pose, [3 1]); - -# t = pg.time(1); -# self.assertClass(t, 'double'); -# self.assertSize(t, [1 1]); - -# w = pg.scanmap('ngrid', 3000); -# self.assertClass(w, 'int32'); -# self.assertSize(w, [3000 3000]); - -# tc.assumeTrue(exist('idisp', 'file')); %REMINDER -# clf -# pg.plot_occgrid(w); -# close all - -# end - -# function makemap_test(tc) -# tc.assumeTrue(false); %REMINDER - -# end - -# function chi2inv_test(tc) -# self.assertEqual( chi2inv_rtb(0,2), 0); -# self.assertEqual( chi2inv_rtb(1,2), Inf); -# self.assertEqual( chi2inv_rtb(3,2), NaN); - -# self.assertError( @() chi2inv_rtb(1,1), 'RTB:chi2inv_rtb:badarg'); - -# end -# - - -if __name__ == "__main__": # pragma nocover +if __name__ == "__main__": unittest.main() - # pytest.main(['tests/test_SerialLink.py']) diff --git a/tests/test_mobile_planner.py b/tests/test_mobile_planner.py index 0d4b543a7..4f23e3afc 100644 --- a/tests/test_mobile_planner.py +++ b/tests/test_mobile_planner.py @@ -2,7 +2,7 @@ import numpy as np import unittest -from roboticstoolbox.mobile import * +from roboticstoolbox.mobile import BinaryOccupancyGrid, DubinsPlanner, ReedsSheppPlanner class TestPlanners(unittest.TestCase): diff --git a/tests/test_trajectory.py b/tests/test_trajectory.py index 594b50ae2..e235774bb 100644 --- a/tests/test_trajectory.py +++ b/tests/test_trajectory.py @@ -269,9 +269,6 @@ def test_mtraj(self): ) ) - # with a time vector - t = np.linspace(0, 2, 11) - tg = tr.mtraj(tr.quintic, q1, q2, 11) q = tg.s qd = tg.sd @@ -375,8 +372,6 @@ def test_mtraj(self): self.assertAlmostEqual(qdd.shape, (11, 6)) # with a time vector - t = np.linspace(0, 2, 11) - tg = tr.mtraj(tr.trapezoidal, q1, q2, 11) q = tg.s qd = tg.sd