Skip to content

Graph is wrong when saved as .eps #14272

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

Open
wolkchen-cirrus opened this issue May 20, 2019 · 10 comments
Open

Graph is wrong when saved as .eps #14272

wolkchen-cirrus opened this issue May 20, 2019 · 10 comments

Comments

@wolkchen-cirrus
Copy link

wolkchen-cirrus commented May 20, 2019

Bug report

Bug summary

Cross-hatching and shaded regions not being rendered when saving a figure as .eps (a format which most journals prefer). pdf and png work fine.

Code is as Follows, cannot show actual data since it is sensitive material at this stage

from matplotlib import pyplot as plt
from matplotlib import font_manager as plt_fnt
from matplotlib import rcParams as mplParams
from matplotlib import lines
from matplotlib.legend import Legend
import numpy as np


plt.style.use("ggplot")
prop = plt_fnt.FontProperties(family=['serif'])
mplParams["font.family"] = prop.get_name()
mplParams['hatch.linewidth'] = 0.5
mplParams['mathtext.default'] = "regular"
mplParams['lines.antialiased'] = False
mplParams['patch.antialiased'] = False


def _gen_dummy_data():
    out = {}
    for i in range(5):
        key = "key" + str(i)
        x = np.arange(0, 5, 0.5).reshape((-1, 1))
        data1 = np.random.normal(np.sin(x), 0.5)
        data2 = data1-0.2
        data3 = data1+0.2
        data = np.hstack((x, data2, data3, data1))
        out[key] = data
    return out


def _cm_to_inch(*tupl):
    inch = 2.54
    if isinstance(tupl[0], tuple):
        return tuple(k/inch for k in tupl[0])
    else:
        return tuple(k/inch for k in tupl)


def _plot_avg_velocity(velocity_arr):

    aoa_lim = 10
    v_mean = 5

    fig = plt.figure()
    fig.set_size_inches(_cm_to_inch(12, 8))
    ax = fig.add_subplot(111)
    title_string = "Radial Velocity of Airflow During CFD Simulations"
    ax.set_title(title_string, fontsize="small")

    marker_style = dict(linestyle='-', marker='', markersize=5, fillstyle='none', color='C0')
    legend1_style = dict(marker='', color='C0', linestyle='-', fillstyle='none')
    fill_style = dict(alpha=0.5, edgecolor='C0', facecolor='C0')

    vel_err = {}
    for key in velocity_arr:
        vel_err[key] = _get_err(velocity_arr[key])

    mean_handles = {}
    leg_names = []
    leg_handles = []
    i = 0

    for key in vel_err:

        legend1_style['color'] = "C" + str(i)
        leg_handle = lines.Line2D([], [], **legend1_style)
        leg_handles.append(leg_handle)
        leg_str = "Radius: %s m" % key
        leg_names.append(str(leg_str))

        marker_style["color"] = "C" + str(i)
        fill_style["edgecolor"] = "C" + str(i)
        fill_style["facecolor"] = "C" + str(i)
        mean_handles[key] = ax.plot(vel_err[key][:, 0], vel_err[key][:, -1], **marker_style)
        ax.fill_between(vel_err[key][:, 0],  vel_err[key][:, 1], vel_err[key][:, 2], **fill_style)
        i += 1

    ax2 = ax.twinx()
    ax_limits = ax.get_ylim()
    ax.set_ylim(ax_limits[0], ax_limits[1] + 0.2)
    ax_limits = ax.get_ylim()
    ax2_limits = _vel_to_aoa(v_mean, ax_limits)
    ax2.set_ylim(ax2_limits[0], ax2_limits[1])
    ax2.grid(b=None)

    ax.autoscale(False)
    ax2.autoscale(False)

    ax2_xlim = ax2.get_xlim()
    ax2.fill([ax2_xlim[0], ax2_xlim[1], ax2_xlim[1], ax2_xlim[0]], [aoa_lim, aoa_lim, ax2_limits[1], ax2_limits[1]],
             fill=False, hatch='\\', edgecolor='0')

    ax2.plot([-10, 10], [0, 0], linestyle='-.', color=[0, 0, 0], linewidth=0.5)

    leg1 = Legend(ax, leg_handles, leg_names, fontsize="small", frameon=False,
                  loc=5, ncol=1)
    ax.add_artist(leg1)

    ax.set_xlabel("Distance Above Rotors (m)", fontsize="small")
    ax.set_ylabel("Radial Velocity ($ms^{-1}$)", fontsize="small")
    ax2.set_ylabel("Equivalent Airflow Angle of Attack ($^{\circ}$)", fontsize="small")

    plt.tight_layout()
    plt.show()
    return


def _get_err(vel_arr):
    err_arr = vel_arr[:, 1:(-1-1)]
    avg_arr = vel_arr[:, -1]
    z_arr = vel_arr[:, 0]
    rows, cols = err_arr.shape
    min_arr = np.zeros([rows])
    max_arr = np.zeros([rows])
    vel_err_arr = np.zeros([rows, 4])
    for i in range(rows):
        min_arr[i] = np.min(err_arr[i, :])
        max_arr[i] = np.max(err_arr[i, :])
    vel_err_arr[:, 0] = z_arr
    vel_err_arr[:, 1] = min_arr
    vel_err_arr[:, 2] = max_arr
    vel_err_arr[:, 3] = avg_arr
    return vel_err_arr


def _vel_to_aoa(v_mean, v):
    if isinstance(v, tuple):
        return tuple(np.arctan(i/v_mean)*180/np.pi for i in v)
    else:
        return np.arctan(v/v_mean)


dat = _gen_dummy_data()
_plot_avg_velocity(dat)

Actual outcome

Unfortunately I am unable to share data from this graph. Dummy data instead:

test_fig

Expected outcome

The graph rendered with cross-hatching and shaded regions

Matplotlib version

  • Operating system: Windows 10 (also tried on scientific linux 7)
  • Matplotlib version: 2.2.4
  • Matplotlib backend (print(matplotlib.get_backend())):
  • Python version: 2.7
  • Jupyter version (if applicable): N/A
  • Other libraries: numpy

Installed matplotlib via pip

@jklymak
Copy link
Member

jklymak commented May 20, 2019

Thanks for the report. Can you make a minimal example that duplicates the problem (though this may be a duplicate). Also matplotlib 3.1 doesn’t work w py2.7 so please check your versions.

@wolkchen-cirrus
Copy link
Author

Thanks for the report. Can you make a minimal example that duplicates the problem (though this may be a duplicate). Also matplotlib 3.1 doesn’t work w py2.7 so please check your versions.

My apologies, the version number is 2.2.4 I will change this now.

@tacaswell
Copy link
Member

From reading the code alpha in your graph which is not supported by eps (at the file specification level).

Could you provide some synthetic data? It is much easier to debug things if we have runnable code to test.

@wolkchen-cirrus
Copy link
Author

wolkchen-cirrus commented May 20, 2019

From reading the code alpha in your graph which is not supported by eps (at the file specification level).

Could you provide some synthetic data? It is much easier to debug things if we have runnable code to test.

Thanks for your response, I have attached some figures and updated the code with dummy data. Unfortunately it seems eps files cannot be uploaded to github so you will have to generate the figure yourself.

test_fig

@efiring
Copy link
Member

efiring commented Jun 2, 2019

Simple example, python 3, mpl 2.2.2 and 3.1.0.post1017+g4b50ce6cc:

import matplotlib.pyplot as plt
x = [0, 1, 1, 0]
y = [0, 0, 1, 1]
plt.fill(x, y, hatch='\\')
plt.savefig('t1.eps')
plt.savefig('t1.pdf')

The hatching appears only in the pdf file.

@QuLogic
Copy link
Member

QuLogic commented Apr 28, 2020

@efiring's example works for me on master; maybe it was fixed, or it's a viewer issue?

@QuLogic
Copy link
Member

QuLogic commented Apr 28, 2020

Actually, it's the opposite; no hatches appear in the pdf (this seems to be a viewer problem.)

@efiring
Copy link
Member

efiring commented Apr 28, 2020

OSX Catalina (10.15.3):

On OSX, preview always converts to pdf before displaying eps. In this case, it displays the original pdf fine, but the pdf it generates from the eps is displayed with no hatching.

On Linux (an Xubuntu VM on VirtualBox), the Atril viewer shows no hatching for the pdf, but does show the hatching for the eps.

So yes, it's viewer dependent, but still points to a bug in our file generation. I wonder whether it is related to zorder, and the differences are in whether the background or the hatch ends up on top.
We should be ensuring the hatch has a slightly larger zorder than the solid fill, but I'll bet they are the same, because hatching is being treated as equivalent to solid filling rather than as a set of lines, which would put it higher.

@QuLogic
Copy link
Member

QuLogic commented Apr 21, 2023

The zorder is not important here; that's a Matplotlib concept, and hatching is a property of an artist itself at the same level as zorder. The vector backends just draw things in the order they're given.

The PostScript and PDF backends are similar but do have one key difference: PS uses an uncolored tiling pattern and sets black as the colour when applying it as strokes over top the rectangle that's already drawn, while PDF uses a colored tiling pattern that includes both the background and the strokes which is applied together to draw the whole rectangle.

@QuLogic
Copy link
Member

QuLogic commented Apr 21, 2023

I wonder if the EPS part was fixed by #15064?

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

No branches or pull requests

6 participants