What is draw_path's transform's destination?

I'm implementing a custom path effect by inheriting from AbstractPathEffect
and implementing draw_path as instructed here
<https://matplotlib.org/api/patheffects_api.html#matplotlib.patheffects.AbstractPathEffect.draw_path>.
That link references RenderBase.draw_path
<https://matplotlib.org/api/backend_bases_api.html#matplotlib.backend_bases.RendererBase.draw_path>
to
define the required interface. Unfortunately, I can't find anywhere in the
associated documentation that tells me what the transform argument really
entails.

I've also read the transforms tutorial
<https://matplotlib.org/tutorials/advanced/transforms_tutorial.html>, and
while that explains the pipeline and all the intermediate transformations,
it doesn't really settle what I have available to me in draw_path. It is
clear that I can use it to transform coordinates from data to something --
but I don't know what to.

For the big picture -- I am implementing a path effect to allow drawing
hatched lines similar to this
<https://www.mathworks.com/matlabcentral/fileexchange/29121-hatched-lines-and-contours>,
which I did years ago in Matlab. Years before that, I've also done this in
Java Graphics2D by implementing a custom stroke. (The Java approach is
more applicable to the path effect approach, but I can't point to that code
online anywhere.)

Anyway, I have things mostly working -- and I am using the transform to go
from data coordinates to something that looks orthogonal and reasonable
on-screen. So, for minimum functionality, it works. However, I want to
give the user the ability to control the length and spacing of the hatches
-- which is in a coordinate system after the transformation is applied.
So, in order to document this thing, I need to know what it is. Or, if
there is a way to get intermediate stages of the transformation pipeline,
that would work too.

As for the user interface -- it seems to make the most sense to specify the
hatch length and spacing in terms of something similar to a line width
(typically points) or a marker size. I'm new to Python and matplotlib --
what is the Pythonic unit a user would expect to specify this in (and how
do I achieve that with what is available in draw_path)?

Thanks,

Rob
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/matplotlib-devel/attachments/20190330/58b2bd30/attachment.html>

The transform is from whatever coordinates the path is in to pixels.

From skimming the code on `Line2D` (and from the name) it looks like it is

only the affine part of the path transformation.

Looking at the method `_offset_transform` in the base class also supports
the target coordinate system of the transform is pixels.

That is a pretty nifty tool, is it going to end up someplace public when
you are done?

Tom

···

On Sat, Mar 30, 2019 at 6:44 PM Rob McDonald <rob.a.mcdonald at gmail.com> wrote:

I'm implementing a custom path effect by inheriting from
AbstractPathEffect and implementing draw_path as instructed here
<https://matplotlib.org/api/patheffects_api.html#matplotlib.patheffects.AbstractPathEffect.draw_path&gt;\.
That link references RenderBase.draw_path
<https://matplotlib.org/api/backend_bases_api.html#matplotlib.backend_bases.RendererBase.draw_path&gt; to
define the required interface. Unfortunately, I can't find anywhere in the
associated documentation that tells me what the transform argument really
entails.

I've also read the transforms tutorial
<https://matplotlib.org/tutorials/advanced/transforms_tutorial.html&gt;, and
while that explains the pipeline and all the intermediate transformations,
it doesn't really settle what I have available to me in draw_path. It is
clear that I can use it to transform coordinates from data to something --
but I don't know what to.

For the big picture -- I am implementing a path effect to allow drawing
hatched lines similar to this
<Hatched Lines and Contours - File Exchange - MATLAB Central,
which I did years ago in Matlab. Years before that, I've also done this in
Java Graphics2D by implementing a custom stroke. (The Java approach is
more applicable to the path effect approach, but I can't point to that code
online anywhere.)

Anyway, I have things mostly working -- and I am using the transform to go
from data coordinates to something that looks orthogonal and reasonable
on-screen. So, for minimum functionality, it works. However, I want to
give the user the ability to control the length and spacing of the hatches
-- which is in a coordinate system after the transformation is applied.
So, in order to document this thing, I need to know what it is. Or, if
there is a way to get intermediate stages of the transformation pipeline,
that would work too.

As for the user interface -- it seems to make the most sense to specify
the hatch length and spacing in terms of something similar to a line width
(typically points) or a marker size. I'm new to Python and matplotlib --
what is the Pythonic unit a user would expect to specify this in (and how
do I achieve that with what is available in draw_path)?

Thanks,

Rob
_______________________________________________
Matplotlib-devel mailing list
Matplotlib-devel at python.org
Matplotlib-devel Info Page

--
Thomas Caswell
tcaswell at gmail.com
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/matplotlib-devel/attachments/20190331/b700c13a/attachment.html&gt;

Thanks for the help,

How are 'pixels' handled when going to a vector back end? I've seen some
points_to_pixels methods around, but that isn't quite right.

Is the best thing to do to allow the user to set the hatch spacing in
pixels?

I'll be happy to make it a contribution to the collection of path effects
included in patheffects.py once I get things working smoothly.

For best effect (for my needs), it will also need to work well with
contours. I just found the docs that show that contours are already
oriented
<https://github.com/matplotlib/matplotlib/blob/master/src/_contour.h#L64&gt;
(great
news - I had to write a contour orienter in Matlab to get consistent
behavior). Unfortunately, naively passing path_effects=[HatchedStroke()]
to a plt.contour() call doesn't 'just work'. Is there a way to tell
contour lines to be drawn with a different path effect?

Contributing this work presents a bit of a naming collision -- I generally
call these hatched lines. I would like to call this HatchedPathEffect or
HatchedStroke when contributing it. However, matplotlib uses hatching to
refer to filling an area with a pattern. While related, this is obviously
different. Should I call it something else? (what is Parseltongue for
HatchedStroke?)

Rob

···

On Sat, Mar 30, 2019 at 11:12 PM Thomas Caswell <tcaswell at gmail.com> wrote:

The transform is from whatever coordinates the path is in to pixels.

From skimming the code on `Line2D` (and from the name) it looks like it is
only the affine part of the path transformation.

Looking at the method `_offset_transform` in the base class also supports
the target coordinate system of the transform is pixels.

That is a pretty nifty tool, is it going to end up someplace public when
you are done?

Tom

On Sat, Mar 30, 2019 at 6:44 PM Rob McDonald <rob.a.mcdonald at gmail.com> > wrote:

I'm implementing a custom path effect by inheriting from
AbstractPathEffect and implementing draw_path as instructed here
<https://matplotlib.org/api/patheffects_api.html#matplotlib.patheffects.AbstractPathEffect.draw_path&gt;\.
That link references RenderBase.draw_path
<https://matplotlib.org/api/backend_bases_api.html#matplotlib.backend_bases.RendererBase.draw_path&gt; to
define the required interface. Unfortunately, I can't find anywhere in the
associated documentation that tells me what the transform argument really
entails.

I've also read the transforms tutorial
<https://matplotlib.org/tutorials/advanced/transforms_tutorial.html&gt;,
and while that explains the pipeline and all the intermediate
transformations, it doesn't really settle what I have available to me in
draw_path. It is clear that I can use it to transform coordinates from
data to something -- but I don't know what to.

For the big picture -- I am implementing a path effect to allow drawing
hatched lines similar to this
<Hatched Lines and Contours - File Exchange - MATLAB Central,
which I did years ago in Matlab. Years before that, I've also done this in
Java Graphics2D by implementing a custom stroke. (The Java approach is
more applicable to the path effect approach, but I can't point to that code
online anywhere.)

Anyway, I have things mostly working -- and I am using the transform to
go from data coordinates to something that looks orthogonal and reasonable
on-screen. So, for minimum functionality, it works. However, I want to
give the user the ability to control the length and spacing of the hatches
-- which is in a coordinate system after the transformation is applied.
So, in order to document this thing, I need to know what it is. Or, if
there is a way to get intermediate stages of the transformation pipeline,
that would work too.

As for the user interface -- it seems to make the most sense to specify
the hatch length and spacing in terms of something similar to a line width
(typically points) or a marker size. I'm new to Python and matplotlib --
what is the Pythonic unit a user would expect to specify this in (and how
do I achieve that with what is available in draw_path)?

Thanks,

Rob
_______________________________________________
Matplotlib-devel mailing list
Matplotlib-devel at python.org
Matplotlib-devel Info Page

--
Thomas Caswell
tcaswell at gmail.com

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/matplotlib-devel/attachments/20190330/0642cc88/attachment-0001.html&gt;

OK -- so I made some progress on this...

cp = plt.contour(x, y, z, colors=('k',), )
plt.setp(cp.collections, path_effects=[HatchedStroke()])

By and large works. However, some of the hatches appear 'off' the lines.
I believe this is because some of the contour Path's might be generated as
Bezier curves (not simple polylines). I need to interpolate along the path
to generate hatches, but I don't have a full renderer -- and
Path.interpolate doesn't do curves. I currently do the following to get to
polylines

newpath = tpath.cleaned()
polys = newpath.to_polygons(closed_only=False)

Which generally works, but the default settings (deep inside agg from what
I can tell (agg_curves.cpp etc.)) produce very coarse approximations that
are rather unsatisfying.

Is there a better way to get a polyline from an arbitrary Path (that may
include Bezier curves)? Someone has to have implemented a compound path
evaluation routine...

Alternately, is there a way to force contour to generate straight-line
segments -- forcing the user to bump up the grid resolution if they want
smooth curves? Actually, that doesn't work. I currently have an example
with very fine resolution -- it appears that something in the Contour Path
hierarchy is being clever and replacing the polyline segments with a smooth
Bezier curve. Possibly via a simplify call -- if there isn't a way to
evaluate the true Path in draw_path, then perhaps there is a way to turn
off the simplify step contour is doing...

Rob
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/matplotlib-devel/attachments/20190331/b5c149d9/attachment.html&gt;

···

On Sat, Mar 30, 2019 at 11:51 PM Rob McDonald <rob.a.mcdonald at gmail.com> wrote:

Unfortunately, naively passing path_effects=[HatchedStroke()] to a
plt.contour() call doesn't 'just work'. Is there a way to tell contour
lines to be drawn with a different path effect?

For the record, I was able to resolve this issue as well.

This SO answer <https://stackoverflow.com/a/44552090&gt; references a response
to a GitHub issue that gives the critical clue -- to_polygons has a
hard-coded resolution of 1 because it expects to work in pixel space. So,
if I transform the Path before evaluating it, I get acceptable results.

Thanks,

Rob

···

On Sun, Mar 31, 2019 at 12:30 AM Rob McDonald <rob.a.mcdonald at gmail.com> wrote:

On Sat, Mar 30, 2019 at 11:51 PM Rob McDonald <rob.a.mcdonald at gmail.com> > wrote:

Unfortunately, naively passing path_effects=[HatchedStroke()] to a
plt.contour() call doesn't 'just work'. Is there a way to tell contour
lines to be drawn with a different path effect?

OK -- so I made some progress on this...

cp = plt.contour(x, y, z, colors=('k',), )
plt.setp(cp.collections, path_effects=[HatchedStroke()])

By and large works. However, some of the hatches appear 'off' the lines.
I believe this is because some of the contour Path's might be generated as
Bezier curves (not simple polylines). I need to interpolate along the path
to generate hatches, but I don't have a full renderer -- and
Path.interpolate doesn't do curves. I currently do the following to get to
polylines

newpath = tpath.cleaned()
polys = newpath.to_polygons(closed_only=False)

Which generally works, but the default settings (deep inside agg from what
I can tell (agg_curves.cpp etc.)) produce very coarse approximations that
are rather unsatisfying.

Is there a better way to get a polyline from an arbitrary Path (that may
include Bezier curves)? Someone has to have implemented a compound path
evaluation routine...

Alternately, is there a way to force contour to generate straight-line
segments -- forcing the user to bump up the grid resolution if they want
smooth curves? Actually, that doesn't work. I currently have an example
with very fine resolution -- it appears that something in the Contour Path
hierarchy is being clever and replacing the polyline segments with a smooth
Bezier curve. Possibly via a simplify call -- if there isn't a way to
evaluate the true Path in draw_path, then perhaps there is a way to turn
off the simplify step contour is doing...

Rob

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/matplotlib-devel/attachments/20190401/a92050fe/attachment.html&gt;

Tom,

Sorry it has taken a while for me to come back to this, but better late than never.

I’ve attached what I came up with. This is essentially my first foray into Python, so it likely has some warts beyond not being very Pythonic.

I haven’t been able to get a local matplotlib build environment working – so these aren’t properly integrated into patheffects.py and test_patheffects.py.

I’m somewhat skeptical that you will want to introduce dependencies on scipy (1D interpolation) but I also didn’t want to reinvent the wheel here. Perhaps matplotlib has a 1D interpolator buried someplace.

Rob

P.S. Nathan and Steph say hi.

(Attachment tickedstroke.py is missing)

(Attachment test_tickedstroke.py is missing)

···

On Sat, Mar 30, 2019 at 9:12 PM Thomas Caswell tcaswell@gmail.com wrote:

The transform is from whatever coordinates the path is in to pixels.

From skimming the code on Line2D (and from the name) it looks like it is only the affine part of the path transformation.

Looking at the method _offset_transform in the base class also supports the target coordinate system of the transform is pixels.

That is a pretty nifty tool, is it going to end up someplace public when you are done?

Tom

On Sat, Mar 30, 2019 at 6:44 PM Rob McDonald rob.a.mcdonald@gmail.com wrote:

I’m implementing a custom path effect by inheriting from AbstractPathEffect and implementing draw_path as instructed here. That link references RenderBase.draw_path to define the required interface. Unfortunately, I can’t find anywhere in the associated documentation that tells me what the transform argument really entails.

I’ve also read the transforms tutorial, and while that explains the pipeline and all the intermediate transformations, it doesn’t really settle what I have available to me in draw_path. It is clear that I can use it to transform coordinates from data to something – but I don’t know what to.

For the big picture – I am implementing a path effect to allow drawing hatched lines similar to this, which I did years ago in Matlab. Years before that, I’ve also done this in Java Graphics2D by implementing a custom stroke. (The Java approach is more applicable to the path effect approach, but I can’t point to that code online anywhere.)

Anyway, I have things mostly working – and I am using the transform to go from data coordinates to something that looks orthogonal and reasonable on-screen. So, for minimum functionality, it works. However, I want to give the user the ability to control the length and spacing of the hatches – which is in a coordinate system after the transformation is applied. So, in order to document this thing, I need to know what it is. Or, if there is a way to get intermediate stages of the transformation pipeline, that would work too.

As for the user interface – it seems to make the most sense to specify the hatch length and spacing in terms of something similar to a line width (typically points) or a marker size. I’m new to Python and matplotlib – what is the Pythonic unit a user would expect to specify this in (and how do I achieve that with what is available in draw_path)?

Thanks,

Rob


Matplotlib-devel mailing list

Matplotlib-devel@python.org

https://mail.python.org/mailman/listinfo/matplotlib-devel


Thomas Caswell
tcaswell@gmail.com