Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork8.1k
Stroked path width#17198
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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
base:main
Are you sure you want to change the base?
Stroked path width#17198
Uh oh!
There was an error while loading.Please reload this page.
Conversation
5a401ee to7d84966Compare7d84966 to0a92c2fCompare0a92c2f to265caecComparejkseppan commentedAug 14, 2020
Looks good to me. I wonder what's going to with the AppVeyor and Azure checks. |
jkseppan left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Approving modulo CI passing
jkseppan commentedAug 14, 2020
jkseppan commentedAug 14, 2020
jkseppan commentedAug 14, 2020
Once this is merged, we could use this in |
QuLogic left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I only really read docs so far.
lib/matplotlib/path.py Outdated
| form a corner, the angle swept out by the two lines that meet at the | ||
| vertex. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I'm confused what this means with relation to the next member, especially since that one hascorner in the name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Good catch. This was bad copy/paste from an older version where the names were too difficult to remember (phi/theta, as below). I fixed the description here to actually make sense.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
| ---------- | ||
| extents : 4*[float] | ||
| The extents (xmin, ymin, xmax, ymax) of the `~.transforms.Bbox` of the | ||
| vertices. Modified in place so that the corner described by *vinfo* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
The type should be more explicit then; it has to a mutable reference of some sort to work (i.e., not tuples.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Is(4,) array_like of float better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Maybe, I'm not sure we have a convention for must-be-mutable,@timhoffm?
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
265caec to48fe47aComparebrunobeltran commentedAug 31, 2020
Thanks@QuLogic as usual for your detailed documentation read, I'm glad you caught these lingering copy/paste errors, as they made the code hard to go back to even for me. Hopefully these documentation fixes help the code make lots more sense now. Let me know if you have any issues parsing what something is supposed to be doing. I assume you'll ask this question when you actually read the code itself: "why did he get the angle information in global coordinates, then include the logic to pretend all corners point in the positive x direction right at the end?" The formulas are much simpler if you fix the corners to be aligned in on specific direction, it turns out. I tried both ways (the other way being to include the switching logic for which direction the corner is pointing directly in the code that's currently in "stroke_x_overflow") and I found this one much more readable. |
48fe47a toefceb57Compareefceb57 todad23baComparebrunobeltran commentedSep 1, 2020 via email
The original idea was that ``4*[float]`` was at least not ``4*(float, )``... …On Mon, Aug 31, 2020 at 4:42 PM Elliott Sales de Andrade < ***@***.***> wrote: ***@***.**** commented on this pull request. ------------------------------ In lib/matplotlib/path.py <#17198 (comment)> : > + elif joinstyle == 'round': + return width/2 # hemispherical cap, so always same padding + else: + raise ValueError(f"Unknown joinstyle: {joinstyle}") + + +def _pad_extents_stroked_vertex(extents, vinfo, markeredgewidth, joinstyle, + capstyle): + """ + Accumulator for building true extents from `.VertexInfo`s. + + Parameters + ---------- + extents : 4*[float] + The extents (xmin, ymin, xmax, ymax) of the `~.transforms.Bbox` of the + vertices. Modified in place so that the corner described by *vinfo* Maybe, I'm not sure we have a convention for must-be-mutable,@timhoffm <https://github.com/timhoffm>? — You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub <#17198 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/AALIGPUEAIH5ZFF7Q26TRTTSDQYPHANCNFSM4MML7XCA> . |
QuLogic left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
This was pretty-well commented, which made it easier to review. I mean, it still took hours, but probably less than if it weren't.
| cap_angles= [first_tan_angle,prev_tan_angle] | ||
| cap_vertices= [first_vertex,prev_vertex] | ||
| forcap_angle,cap_vertexinzip(cap_angles,cap_vertices): | ||
| yieldVertexInfo(cap_vertex,cap_angle,None) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Just yielding the two things seems more straightforward?
| cap_angles= [first_tan_angle,prev_tan_angle] | |
| cap_vertices= [first_vertex,prev_vertex] | |
| forcap_angle,cap_vertexinzip(cap_angles,cap_vertices): | |
| yieldVertexInfo(cap_vertex,cap_angle,None) | |
| yieldVertexInfo(first_vertex,first_tan_angle,None) | |
| yieldVertexInfo(prev_vertex,prev_tan_angle,None) |
| # deal with capping ends of previous polyline, if it exists | ||
| ifprev_tan_angleisnotNoneandis_capped: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Capping occurs if the polyline was unclosed, so I'm 50/50 whetheris_capped should beis_closed (with opposite values), or this comment should mention that.
| forcap_angle,cap_vertexin [(first_tan_angle,first_vertex), | ||
| (prev_tan_angle,prev_vertex)]: | ||
| yieldVertexInfo(cap_vertex,cap_angle,None) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Same here.
| forcap_angle,cap_vertexin [(first_tan_angle,first_vertex), | |
| (prev_tan_angle,prev_vertex)]: | |
| yieldVertexInfo(cap_vertex,cap_angle,None) | |
| yieldVertexInfo(first_vertex,first_tan_angle,None) | |
| yieldVertexInfo(prev_vertex,prev_tan_angle,None) |
| # often CLOSEPOLY is used when the curve has already reached | ||
| # it's initial point in order to prevent there from being a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
| #often CLOSEPOLY is used when the curve has already reached | |
| #it's initial point in order to prevent there from being a | |
| #Often CLOSEPOLY is used when the curve has already reached | |
| #its initial point in order to prevent there from being a |
| ---------- | ||
| markeredgewidth : float | ||
| Width, in points, of the stroke used to create the marker's edge. | ||
| For ``markeredgewidth = 0``, same as `.get_extents`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
| For``markeredgewidth=0``,sameas`.get_extents`. | |
| For``markeredgewidth=0``,theresultwillbecomethesameas`.get_extents`. |
| phi1=phi+theta/2 | ||
| phi2=phi-theta/2 | ||
| return (width/2)*max(np.abs(np.cos(phi1)),np.abs(np.cos(phi2))) | ||
| # finally, _joinstyle = "round" is just _joinstyle = "bevel" but with |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
?
| # finally,_joinstyle = "round" is just_joinstyle = "bevel" but with | |
| # finally,joinstyle = "round" is justjoinstyle = "bevel" but with |
| cap_perp=vinfo.incidence_angle+perp_dir | ||
| x+= (markeredgewidth/2)*np.cos(cap_perp) | ||
| ifx<extents[xmin]: | ||
| extents[xmin]=x | ||
| ifx>extents[xmax]: | ||
| extents[xmax]=x | ||
| y+= (markeredgewidth/2)*np.sin(cap_perp) | ||
| ify<extents[ymin]: | ||
| extents[ymin]=y | ||
| ify>extents[ymax]: | ||
| extents[ymax]=y |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I'm not sure if it looks better this way (ditto the others), or worse and slower...
cap_perp=vinfo.incidence_angle+perp_dirx+= (markeredgewidth/2)*np.cos(cap_perp)extents[xmin]=min(extents[xmin],x)extents[xmax]=max(extents[xmax],x)y+= (markeredgewidth/2)*np.sin(cap_perp)extents[ymin]=min(extents[ymin],y)extents[ymax]=max(extents[ymax],y)
| # of the bbox, and see if the stroked vertex expands the extents... | ||
| x,y=vinfo.apex | ||
| ifnp.cos(vinfo.incidence_angle)>0: | ||
| incidence_angle=vinfo.incidence_angle+np.pi/2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
In these cases, I'd name the local variabletheta, as the reference for the angle is different (I think?), so it's different thanincidence_angle.
| For vertices with one incoming line, *phi* is the incidence angle that | ||
| line forms with the positive y-axis. For corners (vertices with two |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Clockwise, or counter-clockwise?
| @image_comparison(['stroked_bbox'],remove_text=True, | ||
| extensions=['pdf','svg','png']) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
These are the default extensions?
| @image_comparison(['stroked_bbox'],remove_text=True, | |
| extensions=['pdf','svg','png']) | |
| @image_comparison(['stroked_bbox'],remove_text=True) |
Since this Pull Request has not been updated in 60 days, it has been marked "inactive." This does not mean that it will be closed, though it may be moved to a "Draft" state. This helps maintainers prioritize their reviewing efforts. You can pick the PR back up anytime - please ping us if you need a review or guidance to move the PR forward! If you do not plan on continuing the work, please let us know so that we can either find someone to take the PR over, or close it. |



Uh oh!
There was an error while loading.Please reload this page.
PR Summary
Introduces code to
path.pyandbezier.pythat allows us to compute the extents of strokedPaths analytically. Required for bugfix PRs#17182 and#17199, whichfixes#16606.Previous Behavior
The following code should nominally get the extents of the path, but the current
get_extentsonly takes into account the control points:New Behavior
Now we can use the
Path.get_stroked_extentsmethod:Saving as a PDF or PNG produces the following:
Remaining bugs
If we instead output an SVG, it's wrong (because the default miterlimit is different). To fix this, we would need to deal with#9830, because matplotlib doesn't set the miterlimit right now. This feels acceptably out of scope for this PR. This PR contains all the code required to deal with an arbitrary miterlimit, but it has been hardcoded to 10, since that's the nominal miterlimit for Agg, which is in charge of rasterizing, allowing us tofix#17182.
PR Checklist