Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Path.arc is broken for clockwise arcs #13717

Open
@HPLegion

Description

@HPLegion

Bug report

Bug summary

When calling Path.arc in a clockwise fashion (theta1 > theta2) , the angle wrapping results in drawing the arc over the opposing angle in a counterclockwise direction.

Code for reproduction

from matplotlib.path import Path
import matplotlib as mpl
import numpy as np

### Version
print("MPL Version", mpl.__version__)

### Demo the problem
theta1 = -1
theta2 = 1
path = Path.arc(theta1, theta2)
vrt = path.vertices
print(f"Counterclockwise: {len(vrt)} steps.")
theta1 = 1
theta2 = -1
path = Path.arc(theta1, theta2)
vrt = path.vertices
print(f"Clockwise: {len(vrt)} steps.")

### The following code demonstrates what is happening inside Path.arc
theta1 = -1.0
theta2 = 1.0
## Snippet from matplotlib.path.Path.arc
eta1 = theta1
eta2 = theta2 - 360 * np.floor((theta2 - theta1) / 360)
# Ensure 2pi range is not flattened to 0 due to floating-point errors,
# but don't try to expand existing 0 range.
if theta2 != theta1 and eta2 <= eta1:
    eta2 += 360

print(f"Counterclockwise: Input({theta1}, {theta2}), Output({eta1}, {eta2})")

theta1 = 1.0
theta2 = -1.0

eta1 = theta1
eta2 = theta2 - 360 * np.floor((theta2 - theta1) / 360)
# Ensure 2pi range is not flattened to 0 due to floating-point errors,
# but don't try to expand existing 0 range.
if theta2 != theta1 and eta2 <= eta1:
    eta2 += 360

print(f"Clockwise: Input({theta1}, {theta2}), Output({eta1}, {eta2})")

Actual outcome

MPL Version 3.0.3
Counterclockwise: 7 steps.
Clockwise: 49 steps.
Counterclockwise: Input(-1.0, 1.0), Output(-1.0, 1.0)
Clockwise: Input(1.0, -1.0), Output(1.0, 359.0)

Expected outcome

The direction of the arc should not matter, either direction should draw an arc that covers 2 degrees, and the vertices generated by the function should go in the direction chosen by the user.

Proposed fix

For my purposes I fixed the problem by adding another condition into the angle wrapping logic.

    eta1 = theta1
    eta2 = theta2 - 360 * np.floor((theta2 - theta1) / 360)
    # Ensure 2pi range is not flattened to 0 due to floating-point errors,
    # but don't try to expand existing 0 range.
    if theta2 != theta1 and eta2 <= eta1:
        eta2 += 360

   # This is the new logic
    if theta1 > theta2:
        eta1 += 360

    eta1, eta2 = np.deg2rad([eta1, eta2])

This assures that directionality is kept and the correct angle is covered.

Matplotlib version

Probably irrelevant but:

  • Operating system: Windows
  • Matplotlib version: 3.0.3 (conda)
  • Python version: 3.7.0 (conda)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions