Skip to content

"Fillable" markers should be equal-area or equal-width for a given markersize #16623

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

Closed
brunobeltran opened this issue Mar 1, 2020 · 2 comments

Comments

@brunobeltran
Copy link
Contributor

Bug report

Currently, "markersize" means something quite heterogeneous for each different marker (see below). We could easily make it mean the same thing by scaling the "base" size of each marker's path so that it has unit area. This would have the upside of making plots with a constant markersize across different markers look perceptually uniform. And would fix a few ancient issues, like issue #1099.

An alternative would be to have markers be unit "width" (see below), which would fix all but the pentagon markers to at least be slightly more uniform. We could then use the actual areas of the unit marker sizes as a simple scaling factor in Axes.scatter to fix #1099.

Code for reproduction

import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
for marker in mpl.markers.MarkerStyle.markers:
    fig, ax = plt.subplots()
    plt.xlim([-0.04, 0.04])
    plt.ylim([-0.04, 0.04])
    lines = ax.scatter(0, 0, s=100**2, marker=marker, clip_on=False)
    plt.title(str(marker))

    origin_px = ax.transData.transform((0,0))
    top_right_px = origin_px + np.array([50, 50])
    top_right_data = ax.transData.inverted().transform(top_right_px)
    ax.plot([-1, 1], [top_right_data[1], top_right_data[1]])
    ax.plot([-1, 1], [-top_right_data[1], -top_right_data[1]])
    ax.plot([top_right_data[0], top_right_data[0]], [-1, 1])
    ax.plot([-top_right_data[0], -top_right_data[0]], [-1, 1])

Actual outcome

The point is defined to be a half-radius circle. I guess this is fine.
marker-test-point
The pixel of course has a fixed meaning, so need not be updated.
marker-test-pixel

For several markers, markersize means "width" or "height" (many omitted for clarity, but happy to edit this to include all here for reference):

marker-test-caretup

marker-test-circle
marker-test-hexagon1

marker-test-plus_filled
marker-test-square

marker-test-tickright

marker-test-x_filled

For some markers, markersize means "width of square used to create me", which seems pretty unexpected after all the above examples otherwise:
marker-test-diamond
marker-test-thin_diamond

And for only some polygons (notice that there are many full-width triangles in the first list above), markersize means "radius of the circumscribing circle", which means it neither corresponds to the width/height NOR the area:
marker-test-octagon
marker-test-pentagon
marker-test-star
marker-test-tri_down
marker-test-tri_left
marker-test-tri_right
marker-test-tri_up

Expected outcome

Two proposals for fixing this:

  1. make all markersize=1 markers have unit area. This is easy to do by scaling their paths appropriately. The non-fillable markers could be left as-is or scaled to the size at which their "fillable" counterpart would have unit area. (e.g. '1' would be scaled so that the equilateral triangle it draws the skeleton of would have unit area, 'x' and '+' would be scaled so that the squares they draw the skeleton of would have unit area).

  2. make all markersize=1 markers have unit width/height (whichever is larger). This seems more in line with the current "zeitgeist" of marker design. It would only require changing a couple of markers:
    a) 'd' (thin diamond) and 'D' (diamond) would be scaled to be of unit height instead of "D" being made from a rotated unit square (so a factor of 1/sqrt(2)).
    b) '8' (octagon) and 'p'/'*' (pentagon/star) would be scaled up slightly to have unit width. Especially for octagon (see above) this would match MUCH better the current conventions for e.g. 'x'/'X'.
    c) "triangle skeletons" (i.e. 'tri_left' and friends) should be scaled to be the same width as the 'caret' family, for consistency.
    Every other marker could remain the same and this convention be matched.

I am happy to make either of these into a PR (code is written for option 2 already, tbh). However, if we go with option (2), I think we should definitely fix the s argument to scatter to actually be a measurement of the area of the marker in px^2, since each marker in that case will still have a different base area measurement (also code already written, tbh).

Matplotlib version

  • Operating system: Debian 9
  • Matplotlib version: 3.1.3
  • Matplotlib backend (print(matplotlib.get_backend())): N/A
  • Python version: 3.7
  • Jupyter version (if applicable): N/A
  • Other libraries: N/A
@timhoffm
Copy link
Member

timhoffm commented Mar 2, 2020

See also #15703.

@brunobeltran
Copy link
Contributor Author

Closing this, as I basically moved the discussion to #15703. Will happily reopen if e.g. you think that a PR of a "geometrically uniform" marker size (as opposed to a "perceptually uniform" marker size) might be accepted, in which case it might make more sense to refer to this issue directly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants