Skip to content
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

API: change the dot patterns #6547

Merged
merged 2 commits into from Jul 16, 2016

Conversation

tacaswell
Copy link
Member

  • change the dotted pattern to [[1.1, 1.1]]
  • change scale floor to a lw of 2.0

These settings were discussed with @efiring and @jenshnielsen

Some of the test images (+ source) are below.


so

so

import numpy as np
import matplotlib.pyplot as plt
from cycler import cycler
import matplotlib

th = np.linspace(0, 2*np.pi)
fig, ax = plt.subplots()
dot_styles = [[1.2, .6],
              [1.2, 1.2],
              [0.8, 1.2]]

cy = cycler(matplotlib.rcParams['axes.prop_cycle'])
for j, (ds, sty) in enumerate(zip(dot_styles, cy)):
    for k in range(1, 10):
        ax.plot(th, j*np.ones(50) + .1 * k, linestyle=(0, ds),
                label=str((ds, k)), lw=k, **sty)
ax.legend()
import numpy as np
import matplotlib.pyplot as plt
from cycler import cycler
import matplotlib

th = np.linspace(0, 32)
fig, ax = plt.subplots()

dot_size = [.9, 1, 1.1, 1.2]
cy = cycler(matplotlib.rcParams['axes.prop_cycle'])
for j, (ds, sty) in enumerate(zip(dot_size, cy)):
    for lw in np.linspace(.5, 10, 10):
        ax.plot(th, j*np.ones(50) + .1 * lw, linestyle=(0, [ds, ds]),
                label=str((ds, lw)), lw=lw, **sty)

ax.legend()

@tacaswell tacaswell added this to the 2.0 (style change major release) milestone Jun 7, 2016
@afvincent
Copy link
Contributor

How does the new scale floor of lw=2 impact the other dash styles (dashed and dashdot)? With this new behavior, because default lw=1.5, they will now appear doubled compared to what was shown in #5926, won't they? Is it what was wanted?

@jenshnielsen
Copy link
Member

It's only 0.5/1.5 = 1/3 larger at 1.5? But you are right that we should check all of the default patterns.
This should give us some idea

import numpy as np
import matplotlib.pyplot as plt
from cycler import cycler
import matplotlib

th = np.linspace(0, 32)
fig, ax = plt.subplots()

lins_styles = ['dashed', 'dotted', 'dashdot']
cy = cycler(matplotlib.rcParams['axes.prop_cycle'])
for j, (ls, sty) in enumerate(zip(lins_styles, cy)):
    for lw in np.linspace(.5, 10, 10):
        ax.plot(th, j*np.ones(50) + .1 * lw, linestyle=ls,
                label=str((ls, lw)), lw=lw, **sty)

ax.legend()

figure_1

@afvincent
Copy link
Contributor

@jenshnielsen You're right, I didn't take into account that previously the scale was already 1.5. So indeed the impact of the new PR shouldn't be that important.

@@ -897,7 +897,7 @@ def validate_hist_bins(s):
'lines.solid_capstyle': ['projecting', validate_capstyle],
'lines.dashed_pattern': [[2.8, 1.2], validate_nseq_float()],
'lines.dashdot_pattern': [[4.8, 1.2, 0.8, 1.2], validate_nseq_float()],
'lines.dotted_pattern': [[1.2, 0.6], validate_nseq_float()],
'lines.dotted_pattern': [[1.1, 1.1], validate_nseq_float()],
Copy link
Member

Choose a reason for hiding this comment

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

In the line above, I expect the second half of the dashdot pattern should match the dot pattern, or if anything, should be even a little more stretched out along the line. The 0.8 can go to 1.2.

@afvincent
Copy link
Contributor

If it is still time to propose a cosmetic enhancement about the named dash styles, I would like to note that something I find a bit ugly about the current dash style defaults is that there are not very congruent. Because of that, they appear a bit not "homogeneous" in the legend: the dash handle doesn't seem to have the same length as the dotted one (one finishes with ink while the other does with blank), as well as the dotted one vs the dashdot one. Here is a small picture of what I mean by congruent patterns:
pattern_congruence_example
I'm not saying this should be the main criterium to define the dash styles, but IMHO, it might be nice to have default dash styles that are L-congruent (L a length to define), and a default handle length for the legend that shows exactly L or 2*L (with default linewidth).

For the record, here is the code used to find congruent sequence and plot the example. Please note that the constraints I used for the demo might not be the best ones (especially the dash in dashdot is significantly shorter than the current one)...

import matplotlib.pyplot as plt
import numpy as np

"""
*More or less* the current pattern styles
"""
default_lengths = {'blank': 1.2, 'dot': 1.2, 'dash': 2.8, 'long_dash': 4.8,
                   'short_dot': 1.2}

"""
Solve a set of constraints on pattern lengths for a better congruence
"""
# col0: blank (common to all styles)
# col1: simple dot (from 'dotted')
# col2: simple dash (from 'dashed')
# col3: long dash (from 'dashdot')
# col4: short dot (from 'dashdot')
# with L a common length for the three default patterns
L = 100.0  # percent
AA = np.array([[2.,  3.,  0.,  0.,  0.],  # dot - blank - dot - blank - dot = L
               [1.,  0.,  2.,  0.,  0.],  # dash - blank - dash = L
               [1.,  0.,  0.,  1.,  1.],  # long_dash - blank - short_dot = L
               [1., -1.,  0.,  0.,  0.],  # blank = dot
               [0.,  0.,  0.,  1., -3]])  # long_dash = 4 * short_dot
bb = np.array([L, L, L, 0, 0])
xx = np.linalg.solve(AA, bb)
print(xx)  # DEBUG

ref_scale = default_lengths['blank']
patterns_lengths = {key: val for key, val in zip(
                    ('blank', 'dot', 'dash', 'long_dash', 'short_dot'),
                    list(ref_scale * xx / xx.min()))}


def add_pattern(ax, ink_length, blank_length, xinit, yval, color='Black'):
    """ Plot one pattern (ink, empty) """
    # Ink
    ax.plot([xinit, xinit + ink_length], [yval, yval], color=color, lw=2)
    # Blank
    xfinal = xinit + ink_length + blank_length
    return ax, xfinal


def draw_dashed(ax, yval, xstart=0., nb_patterns=12, color='Black',
                p_lengths=patterns_lengths):
    """ Plot a given amount of dash patterns """
    x = xstart
    for j in range(nb_patterns):
        ax, x = add_pattern(ax, p_lengths['dash'], p_lengths['blank'],
                            x, yval, color=color)
    return ax


def draw_dotted(ax, yval, xstart=0., nb_patterns=18, color='Black',
                p_lengths=patterns_lengths):
    """ Plot a given amount of dotted patterns """
    x = xstart
    for j in range(nb_patterns):
        ax, x = add_pattern(ax, p_lengths['dot'], p_lengths['blank'],
                            x, yval, color=color)
    return ax


def draw_dashdot(ax, yval, xstart=0., nb_patterns=6, color='Black',
                 p_lengths=patterns_lengths):
    """ Plot a given amount of dashdot patterns """
    x = xstart
    for j in range(nb_patterns):
        ax, x = add_pattern(ax, p_lengths['long_dash'], p_lengths['blank'],
                            x, yval, color=color)
        ax, x = add_pattern(ax, p_lengths['short_dot'], p_lengths['blank'],
                            x, yval, color=color)
    return ax


def plot_example_set(ax, p_lengths, color='Black', yoffset=0.0, title=""):
    """ Plot an example showing all the named dash styles """
    ax = draw_dotted(ax, 0.25 + yoffset, p_lengths=p_lengths, color=color)
    ax = draw_dashed(ax, 0.50 + yoffset, p_lengths=p_lengths, color=color)
    ax = draw_dashdot(ax, 0.75 + yoffset, p_lengths=p_lengths, color=color)

    # What the handle length of legend entry should at least show
    default_handlelength = (p_lengths['long_dash'] +
                            p_lengths['blank'] +
                            p_lengths['short_dot'])
    ax.axvline(0.0, color='0.5')
    ax.axvline(default_handlelength, color='0.5')

    ax.set_ylim([0, 1])
    ax.set_title(title)
    return ax


if __name__ == '__main__':

    fig, (ax0, ax1) = plt.subplots(nrows=2, num='Demo', sharex=True)
    ax0 = plot_example_set(ax0, default_lengths, color='Crimson',
                           title='~ current default (poor congruence)')
    ax1 = plot_example_set(ax1, patterns_lengths, color='CornFlowerBlue',
                           title='Possible solution for congruent patterns')

@tacaswell
Copy link
Member Author

@afvincent That seems like a reasonable approach to me. One concern is that the pattern now scales with linewidth so picking the length for the legend gets tricky.

@WeatherGod
Copy link
Member

That said... if the problem about the scaling the pattern to the linewidth
could be solved, then this would also solve the problem we are having with
the lines in the legend not always showing the entire dot pattern.

On Thu, Jun 9, 2016 at 8:26 AM, Thomas A Caswell notifications@github.com
wrote:

@afvincent https://github.com/afvincent That seems like a reasonable
approach to me. One concern is that the pattern now scales with linewidth
so picking the length for the legend gets tricky.


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
#6547 (comment),
or mute the thread
https://github.com/notifications/unsubscribe/AARy-E4tt_hQkPCih6vVBO6kmyMdL5msks5qKAZjgaJpZM4IviNM
.

@tacaswell
Copy link
Member Author

We could scale the length of each handle also by lw, but then if you have
mixed lw in your legend your handle sizes will be mixed which seems worse
to me.

On Thu, Jun 9, 2016, 09:35 Benjamin Root notifications@github.com wrote:

That said... if the problem about the scaling the pattern to the linewidth
could be solved, then this would also solve the problem we are having with
the lines in the legend not always showing the entire dot pattern.

On Thu, Jun 9, 2016 at 8:26 AM, Thomas A Caswell <notifications@github.com

wrote:

@afvincent https://github.com/afvincent That seems like a reasonable
approach to me. One concern is that the pattern now scales with linewidth
so picking the length for the legend gets tricky.


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<
#6547 (comment)
,
or mute the thread
<
https://github.com/notifications/unsubscribe/AARy-E4tt_hQkPCih6vVBO6kmyMdL5msks5qKAZjgaJpZM4IviNM

.


You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
#6547 (comment),
or mute the thread
https://github.com/notifications/unsubscribe/AAMMhUiTlXLyHZ9LYG4SSu1rFYSpgH9Yks5qKBapgaJpZM4IviNM
.

@afvincent
Copy link
Contributor

My genuine proposition was simply to have a set of name "dash" styles that are congruent, and show an integer number of "patterns¹" with the default linewidth. Doing that, people that play only with the colors and the markers would have more regular legend handles. And for the others who play also with the named dash styles, they could simply increase the handle length to match something better (like for example the "lowest common multiple" of "patterns"), if they want to. And for the hardcore ones who also use user-defined dash styles, well the new situation wouldn't be worse or better than before...

For the record, my first guess was that a default handle length of 2 "patterns" would be nice, because I was thinking that if lw=1 corresponds to N=2 "patterns", then lw=2 would be OK for N=1 "pattern" with the same handle length. But then I saw that the default lw is 1.5 and the new scale floor is lw=2 so this cannot work this way...

I didn't even bother trying to propose something about tuning the length of the handle, as I expect that a consensus about what would be the better default behavior may be difficult to achieve.

¹ : beware it's not really a "pattern" (meaning a period)! It's just that I find that the case were the handles have ink on both ends more pleasant to the eye. So for example the handle for the dashdot would have a length of N*(dash + blank + dot).

@efiring
Copy link
Member

efiring commented Jun 13, 2016

dot: 1.2, 1.2
dash: 2.4, 1.2
dash-dot: 3.6, 1.2, 1.2, 1.2

@efiring
Copy link
Member

efiring commented Jun 19, 2016

This will address #6515.

@tacaswell
Copy link
Member Author

Updated to use the patterns @efiring listed.

Re-running @jenshnielsen 's

so

@mdboom
Copy link
Member

mdboom commented Jun 28, 2016

👍 from me. This all seems very well-reasoned. Closed/opened to force Travis to rerun -- not sure why that one failure only on Python 2.7 is appearing.

 - change the dotted pattern to [[1.1, 1.1]]
 - change scale floor to a lw of 2.0
@WeatherGod
Copy link
Member

similar failures in py2.7, 3.4 and 3.5. A whole bunch of them, too.

@tacaswell
Copy link
Member Author

Restored the masking for non-'classic' style sheet, will update the images in another PR.

@WeatherGod WeatherGod merged commit 15905ce into matplotlib:v2.x Jul 16, 2016
@tacaswell tacaswell deleted the sty_tweak_dot_pattern branch July 18, 2016 21:58
@QuLogic
Copy link
Member

QuLogic commented Aug 31, 2016

will update the images in another PR.

Does this still need to be done?

@tacaswell
Copy link
Member Author

That is #6757 which is failing and I have not sorted out why yet.

QuLogic added a commit to tacaswell/matplotlib that referenced this pull request Sep 9, 2016
The lines in these test imagee have a different dash style now due
to matplotlib#6547.
QuLogic added a commit to tacaswell/matplotlib that referenced this pull request Sep 10, 2016
The lines in these test imagee have a different dash style now due
to matplotlib#6547.
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.

None yet

7 participants