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

Gouraud shading with tripcolor renders incorrectly as SVG in Jupyter notebook #11321

Open
CorbinFoucart opened this issue May 27, 2018 · 12 comments

Comments

@CorbinFoucart
Copy link

Bug report

Bug summary

When the matplotlib backend is set to 'SVG' in a Jupyter Notebook, it seems that matplotlib does not render 'gouraud' shading correctly.

Code for reproduction

I've uploaded a minimal example of my problem here:
https://nbviewer.jupyter.org/github/CorbinFoucart/FEMexperiment/blob/master/app/mpl_bug.ipynb

Actual outcome

Note the 'gouraud' shading with SVG in the notebook. If I save the output to file and view the output, it is similarly not shaded, which indicates to me that this is not a Jupyter issue, nor a browser issue.

Matplotlib version

  • Operating system: OSX El Capitan 10.11.6
  • Matplotlib version: 2.2.2
  • Matplotlib backend (print(matplotlib.get_backend())): module://ipykernel.pylab.backend_inline
  • Python version: 3.6.4
  • Jupyter version:
ipykernel==4.8.2
jupyter==1.0.0
jupyter-client==5.2.3
jupyter-console==5.2.0
jupyter-core==4.4.0

I installed matplotlib in a virtual environment with pip.

@WeatherGod
Copy link
Member

WeatherGod commented May 29, 2018 via email

@tacaswell tacaswell added this to the v2.2.3 milestone May 29, 2018
@tacaswell
Copy link
Member

It has the right methods,

def draw_gouraud_triangle(self, gc, points, colors, trans):
# This uses a method described here:
#
# http://www.svgopen.org/2005/papers/Converting3DFaceToSVG/index.html
#
# that uses three overlapping linear gradients to simulate a
# Gouraud triangle. Each gradient goes from fully opaque in
# one corner to fully transparent along the opposite edge.
# The line between the stop points is perpendicular to the
# opposite edge. Underlying these three gradients is a solid
# triangle whose color is the average of all three points.
writer = self.writer
if not self._has_gouraud:
self._has_gouraud = True
writer.start(
'filter',
id='colorAdd')
writer.element(
'feComposite',
attrib={'in': 'SourceGraphic'},
in2='BackgroundImage',
operator='arithmetic',
k2="1", k3="1")
writer.end('filter')
avg_color = np.sum(colors[:, :], axis=0) / 3.0
# Just skip fully-transparent triangles
if avg_color[-1] == 0.0:
return
trans_and_flip = self._make_flip_transform(trans)
tpoints = trans_and_flip.transform(points)
writer.start('defs')
for i in range(3):
x1, y1 = tpoints[i]
x2, y2 = tpoints[(i + 1) % 3]
x3, y3 = tpoints[(i + 2) % 3]
c = colors[i][:]
if x2 == x3:
xb = x2
yb = y1
elif y2 == y3:
xb = x1
yb = y2
else:
m1 = (y2 - y3) / (x2 - x3)
b1 = y2 - (m1 * x2)
m2 = -(1.0 / m1)
b2 = y1 - (m2 * x1)
xb = (-b1 + b2) / (m1 - m2)
yb = m2 * xb + b2
writer.start(
'linearGradient',
id="GR%x_%d" % (self._n_gradients, i),
x1=short_float_fmt(x1), y1=short_float_fmt(y1),
x2=short_float_fmt(xb), y2=short_float_fmt(yb))
writer.element(
'stop',
offset='0',
style=generate_css({'stop-color': rgb2hex(c),
'stop-opacity': short_float_fmt(c[-1])}))
writer.element(
'stop',
offset='1',
style=generate_css({'stop-color': rgb2hex(c),
'stop-opacity': "0"}))
writer.end('linearGradient')
writer.element(
'polygon',
id='GT%x' % self._n_gradients,
points=" ".join([short_float_fmt(x)
for x in (x1, y1, x2, y2, x3, y3)]))
writer.end('defs')
avg_color = np.sum(colors[:, :], axis=0) / 3.0
href = '#GT%x' % self._n_gradients
writer.element(
'use',
attrib={'xlink:href': href,
'fill': rgb2hex(avg_color),
'fill-opacity': short_float_fmt(avg_color[-1])})
for i in range(3):
writer.element(
'use',
attrib={'xlink:href': href,
'fill': 'url(#GR%x_%d)' % (self._n_gradients, i),
'fill-opacity': '1',
'filter': 'url(#colorAdd)'})
self._n_gradients += 1
def draw_gouraud_triangles(self, gc, triangles_array, colors_array,
transform):
attrib = {}
clipid = self._get_clip(gc)
if clipid is not None:
attrib['clip-path'] = 'url(#%s)' % clipid
self.writer.start('g', attrib=attrib)
transform = transform.frozen()
for tri, col in zip(triangles_array, colors_array):
self.draw_gouraud_triangle(gc, tri, col, transform)
self.writer.end('g')

but claims to not support it

self._has_gouraud = False

There probably needs to be some git-forensics to sort the history out here.

@avinashmnit30
Copy link
Contributor

avinashmnit30 commented May 29, 2018

The problem is with the SVG backend. Its giving correct output with cario backend. Here's the mplcairo implementation to generate SVG file and it seems to work.

import numpy as np
import matplotlib as mpl
mpl.use("module://mplcairo.qt")
import matplotlib.pyplot as plt
import matplotlib.tri as mtri 
from cairocffi import *
#%matplotlib notebook
#plt.switch_backend('mplcairo')
print(mpl.get_backend())
%config InlineBackend.figure_format = 'svg'
# Mesh
N = 10
x, y = np.linspace(0, 1, N), np.linspace(0, 1, N)
X, Y = np.meshgrid(x, y)

# simple function over the mesh
Z = X**2 + Y**2

# triangulate
tri = mtri.Triangulation(X.ravel(), Y.ravel())
plt.Figure()
# plot
fig, ax = plt.subplots(1, 2)


# default
ax = fig.add_subplot(1, 2, 1)
ax.tripcolor(tri, Z.ravel(), 
                   cmap=plt.cm.coolwarm)
ax.set_title('default')
ax.axis('equal')


# gouraud
ax = fig.add_subplot(1, 2, 2)
ax.tripcolor(tri, Z.ravel(), 
                   cmap=plt.cm.coolwarm, 
                   shading='gouraud')
ax.set_title('gouraud')
ax.axis('equal')
plt.savefig('outputmplqt.svg')
#plt.savefig('outputmplqt.png')
plt.show()

@tacaswell tacaswell modified the milestones: v2.2.3, v3.1 Jun 2, 2018
@tacaswell
Copy link
Member

@avinashmnit30 Is a diff of the two svg files understandable?

@avinashmnit30
Copy link
Contributor

avinashmnit30 commented Jun 5, 2018

@tacaswell "Cario backend" is using PNG image within <image> element of the SVG. So, it's not actually generating Gouraud-triangles in SVG.
Also, the "SVG backend" implementation of Gouraud triangles is incorrect. Its mentioned that it is following http://www.svgopen.org/2005/papers/Converting3DFaceToSVG/index.html paper. But the implementation is incorrect.

Example:

Code for reproduction: https://matplotlib.org/2.0.1/examples/pylab_examples/quadmesh_demo.html

SVG Backend Output:
incorrect_svg

Using correct implementation:
correct_svg

@jklymak
Copy link
Member

jklymak commented Feb 4, 2019

Is this still an open issue given #11378?

@jklymak jklymak modified the milestones: v3.1.0, v3.2.0 Feb 4, 2019
@timhoffm
Copy link
Member

timhoffm commented Feb 16, 2019

This is the output on current master.

grafik

The non-masked version is now correct. However, there is a difference for triangles that are adjacent to the masked area: Our SVG implementation does not add transparency to these triangles. Probably it should, but I'm not an expert in these things.

@anntzer
Copy link
Contributor

anntzer commented Feb 16, 2019

fwiw cairo (and thus mplcairo) renders this "correctly" as SVG, but does so by rasterizing the entire mesh.

@tacaswell tacaswell modified the milestones: v3.2.0, needs sorting Sep 10, 2019
@cover-me
Copy link
Contributor

The current version of svg_backend doesn't work. I think rasterizing the mesh is the only way to achieve Gouraud shading in SVG files at this moment. Because even if triangles get right Gouraud colors, the anti-aliasing issue (#5694) between two adjacent patches would out make the output useless.

Something like this:

Screenshot 2020-04-15 at 19 07 01

@github-actions
Copy link

github-actions bot commented May 8, 2023

This issue has been marked "inactive" because it has been 365 days since the last comment. If this issue is still present in recent Matplotlib releases, or the feature request is still wanted, please leave a comment and this label will be removed. If there are no updates in another 30 days, this issue will be automatically closed, but you are free to re-open or create a new issue if needed. We value issue reports, and this procedure is meant to help us resurface and prioritize issues that have not been addressed yet, not make them disappear. Thanks for your help!

@github-actions github-actions bot added the status: inactive Marked by the “Stale” Github Action label May 8, 2023
@anntzer
Copy link
Contributor

anntzer commented May 9, 2023

I think this is still valid.

@github-actions github-actions bot removed the status: inactive Marked by the “Stale” Github Action label May 10, 2023
@katya-zossi
Copy link

I recently had an issue with svg format and 'gouraud' shading.
Thanks for the tip to add 'rasterized=True' to the tripcolor() function.

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

10 participants