Skip to content

Wedge not honouring specified angular range #3298

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 19, 2014

Conversation

mdboom
Copy link
Member

@mdboom mdboom commented Jul 28, 2014

The following code illustrates the issue. The only difference in the two figures is that there is added decimal places to the precision of the angular range. This added precision however results in complete failure to draw the wedge correctly - a disc is drawn instead. It is repeatable, and tested to be faulty in matplotlib.version '1.2.0' as well as '1.3.1'

import matplotlib
import matplotlib.pyplot as plt

f1=figure(1)
f1.clf()
f1a=f1.gca()
f1a.add_artist(matplotlib.patches.Wedge((265.69040472128086,268.8466729614438), 100, 52.31386924,232.31386924,color='c'))
plt.title('correct sector')
plt.xlim([-500,500])
plt.ylim([-500,500])
plt.draw()

f2=figure(2)
f2.clf()
f2a=f2.gca()
f2a.add_artist(matplotlib.patches.Wedge((265.69040472128086,268.8466729614438), 100, 52.313869244286224,232.31386924428622,color='c'))
plt.title('incorrect sector')
plt.xlim([-500,500])
plt.ylim([-500,500])
plt.draw()

[TAC - edited to fix yelling/formatting

@tacaswell tacaswell added this to the v1.4.x milestone Jul 24, 2014
@tacaswell
Copy link
Member

I am pretty sure that this is an issue with float rounding/comparison and generating the path. Below is a modified example which I think shows the bug more clearly:

import matplotlib
import matplotlib.pyplot as plt

fig, ax = plt.subplots()

w1 = matplotlib.patches.Wedge((0, 0), 1,
                              52.31386924,
                              232.31386924,
                              facecolor='none', edgecolor='k', lw=3)

w2 = matplotlib.patches.Wedge((3, 0), 1,
                             52.313869244286224,
                             232.31386924428622,
                             facecolor='none', edgecolor='k', lw=3)
t1 = 2.313869244286224
w3 = matplotlib.patches.Wedge((6, 0), 1,
                             t1,
                             t1 + 180,
                             facecolor='none', edgecolor='k', lw=3)
ax.add_artist(w1)
ax.add_artist(w2)
ax.add_artist(w3)
ax.set_xlim([-2,8])
ax.set_ylim([-2,2])
ax.set_aspect('equal')

gh

@mattieudv
Copy link
Author

Hi

I have chatted to Ludwig Schwardt about this who will submit a pull request
about this issue shortly.

Cheers
Mattieu

On Thu, Jul 24, 2014 at 2:35 PM, Thomas A Caswell notifications@github.com
wrote:

I am pretty sure that this is an issue with float rounding/comparison and
generating the path. Below is a modified example which I think shows the
bug more clearly:

import matplotlib
import matplotlib.pyplot as plt

fig, ax = plt.subplots()

w1 = matplotlib.patches.Wedge((0, 0), 1,
52.31386924,
232.31386924,
facecolor='none', edgecolor='k', lw=3)

w2 = matplotlib.patches.Wedge((3, 0), 1,
52.313869244286224,
232.31386924428622,
facecolor='none', edgecolor='k', lw=3)
t1 = 2.313869244286224
w3 = matplotlib.patches.Wedge((6, 0), 1,
t1,
t1 + 180,
facecolor='none', edgecolor='k', lw=3)
ax.add_artist(w1)
ax.add_artist(w2)
ax.add_artist(w3)
ax.set_xlim([-2,8])
ax.set_ylim([-2,2])
ax.set_aspect('equal')

[image: gh]
https://cloud.githubusercontent.com/assets/199813/3687890/026cf794-132f-11e4-83dc-9b2248e82ee3.png


Reply to this email directly or view it on GitHub
#3298 (comment)
.

@ludwigschwardt
Copy link

The problem is actually with Path.arc... Try this snippet:

import matplotlib

arc = matplotlib.path.Path.arc
arc(52.31386924,232.31386924)
arc(52.313869244286224,232.31386924428622)

The second arc does an extra loop around the circle.

I've traced the problem to line 835 in path.py (7-year old @mdboom code!) - the last if-statement in the snippet below. The test is False for the first arc and True for the second arc.

    @classmethod
    def arc(cls, theta1, theta2, n=None, is_wedge=False):
        """
        Return an arc on the unit circle from angle
        *theta1* to angle *theta2* (in degrees).
        ....
        """
        # degrees to radians
        theta1 *= np.pi / 180.0
        theta2 *= np.pi / 180.0

        twopi  = np.pi * 2.0
        halfpi = np.pi * 0.5

        eta1 = np.arctan2(np.sin(theta1), np.cos(theta1))
        eta2 = np.arctan2(np.sin(theta2), np.cos(theta2))
        eta2 -= twopi * np.floor((eta2 - eta1) / twopi)
        if (theta2 - theta1 > np.pi) and (eta2 - eta1 < np.pi):
            eta2 += twopi

This is one of those nasty trig corner cases... The if-statement ostensibly caters for the case (theta1, theta2) = (0, 360) or similar, where the user wants the full circle but could get a very thin sliver instead due to the angles getting normalised to the range -180 to 180 degrees (the purpose of eta1 and eta2).

While the example theta1 and theta2 differ by exactly 180 degrees in both cases, after conversion to radians the first case differs by minutely less than pi and the second case by minutely more than pi.

The original (theta1, theta2) arc setup has a sensitive spot around 0 degrees difference, since you either get a single point or a full circle, depending on whether theta1 is bigger or smaller than theta2.
The floating-point test introduces a new sensitive spot around +180 degrees difference. This should have been the most stable part of the arc specification, but now it isn't...

Part of the problem is that it is not clear how the arc is intended to map onto the theta1, theta2 range. Should the arc always stick to a full circle or less, as the case of eta2 - eta1 > 360 degrees is visually indistinguishable from eta2 - eta1 = 360 degrees (or eta1=0, eta2=360 for that matter)? Or should it preserve the number of turns and start and end points, i.e. a full mapping of the sweep? Or just the start and end points with a minimal sweep in between?

That's why I'll leave a pull request to the experts for now, as the specification of arc is unclear and it will affect how this test could be improved :-)

@tacaswell
Copy link
Member

@ludwigschwardt Thanks for tracking this down so well.

@mdboom
Copy link
Member

mdboom commented Jul 28, 2014

@ludwigschwardt: Yes, thanks also from me. To answer your question about intended behavior: I think it should never be more than 360 degrees. It's for drawing, so anything not visible is not necessary. But it should not be the minimal path between starting and ending points, it always should go from theta1 counterclockwise to theta2. Now it's just a matter of fixing the implementation to follow that...

y = i // 3

wedge = mpatches.Wedge(
(x * 3, y * 3), 1, theta1, theta2, facecolor='none', edgecolor='k', lw=3)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is too long

@tacaswell tacaswell self-assigned this Oct 18, 2014
@tacaswell tacaswell merged commit ec2aff4 into matplotlib:master Oct 19, 2014
tacaswell added a commit that referenced this pull request Oct 19, 2014
BUG : Wedge not honouring specified angular range

Fixed minor PEP8 as part of merge
tacaswell added a commit that referenced this pull request Oct 19, 2014
BUG : Wedge not honouring specified angular range

Fixed minor PEP8 as part of merge
@tacaswell
Copy link
Member

Merged in 5b398e9, cherry-picked to 1.4.x as 9c292f4

@mdboom mdboom deleted the arc-bug branch March 3, 2015 18:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants