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

Add possibility of providing triangles while plotting tricontour #12

Closed
thibaultDrt opened this issue Jan 7, 2025 · 7 comments
Closed

Comments

@thibaultDrt
Copy link

thibaultDrt commented Jan 7, 2025

Hello there,

I revive a ProPlot issue that is quite limiting in my field.

Context

In Matplotlib, it is possible to plot contours or filled contours using unstructured grid with triangles, using tricontour and tricontourf.
The documentation shows that several ways of providing arguments are available :

ax.tricontourf(x, y, z, ...)
ax.tricontourf(x, y, triangles, z, ...)
ax.tricontourf(triangulation, z, ...)
  1. In the first case, no connectivity is provided and a Delaunay triangulation algorithm is used internally to create triangles. When dealing with concave geometries, this algorithm fills the empty spaces with undesired triangles, and therefore requires to provide the connectivities.
  2. In the second case, an array of connectivity is provided, allowing to deal with concave geometries for example.
  3. In the 3rd case, an object Triangulation is provided, which mainly consists of coordinates and connectivities.

The management of the arguments (with or without the triangles connectivities array) is dealt here in matplotlib.

Anomaly in UltraPlot

However, in pro-ultraplot, tricontour and its filled counterpart only considers (x, y) arguments, and forces the Delaunay triangulation algorithm to be used.
It would be great to modify this behaviour and allow to provide the triangles array or triangulation object as argument of the function.

Thanks a lot for the help :)

@cvanelteren
Copy link
Contributor

Can have a look!

@cvanelteren
Copy link
Contributor

cvanelteren commented Jan 7, 2025

Could you provide some test code for me to verify the issue @thibaultDrt?
EDIT: for example the issue outlined by you above.

@thibaultDrt
Copy link
Author

Thanks for your answer. Here is a simple test code that runs through matplotlib for ways 1 to 3 and only on ultra-lot for way 1.
The case consists in 2 triangles highlighting a concave shape.

import matplotlib.pyplot as plt
import matplotlib.tri as tri
import numpy as np
x = np.array([0, 1, 0, 0, 1])
y = np.array([0, 0, 1, 2, 2])
z = np.array([0, 1, -1, 0, 2])

conn = np.array([
    [0, 1, 2],
    [2, 3, 4],
])

Using Matplotlib

Here, 3 triangles are created since no connectivity is provided.

fig, ax = plt.subplots(figsize=(4, 3))
ax.tricontourf(x, y, z, levels=64, cmap="PuBu")
ax.triplot(x, y, "ko-") # To display cell edges
[<matplotlib.lines.Line2D at 0x11bba6190>,
 <matplotlib.lines.Line2D at 0x11bbba590>]

ultraplot_issues_tricontourf_2_1

By providing the connectivity with only 2 triangles, only these triangles are shown.

fig, ax = plt.subplots(figsize=(4, 3))
ax.tricontourf(x, y, conn, z, levels=64, cmap="PuBu")
ax.triplot(x, y, conn, "ko-") # To display cell edges
[<matplotlib.lines.Line2D at 0x11bce6c50>,
 <matplotlib.lines.Line2D at 0x11bce7390>]

ultraplot_issues_tricontourf_3_1

Same using the Triangulation object.

triangulation = tri.Triangulation(x, y, conn)

fig, ax = plt.subplots(figsize=(4, 3))
ax.tricontourf(triangulation, z, levels=64, cmap="PuBu")
ax.triplot(triangulation, "ko-")  # To display cell edges
[<matplotlib.lines.Line2D at 0x11bd6cf10>,
 <matplotlib.lines.Line2D at 0x11bdf6a50>]

ultraplot_issues_tricontourf_4_1

Using UltraPlot

import ultraplot as uplot
fig, ax = uplot.subplot()
ax.tricontourf(x, y, z, cmap="PuBu", levels=64, ec="none")
ax.triplot(x, y)
[<matplotlib.lines.Line2D at 0x11c32f590>,
 <matplotlib.lines.Line2D at 0x11b828c90>]

ultraplot_issues_tricontourf_6_1

Here, the connectivity array argument is not allowed because the positional arguments are necessarily x, y and z.

fig, ax = uplot.subplot()
ax.tricontourf(x, y, conn, z, cmap="PuBu", levels=64, ec="none")
ax.triplot(x, y, conn)
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

Cell In[28], line 2
      1 fig, ax = uplot.subplot()
----> 2 ax.tricontourf(x, y, conn, z, cmap="PuBu", levels=64, ec="none")
      3 ax.triplot(x, y, conn)


File ~/.venv/lib/python3.11/site-packages/ultraplot/internals/inputs.py:327, in _preprocess_or_redirect.<locals>._decorator.<locals>._preprocess_or_redirect(self, *args, **kwargs)
    325     kwargs["color"] = color
    326 # Call main function
--> 327 return func(self, *args, **kwargs)


TypeError: PlotAxes.tricontourf() takes 4 positional arguments but 5 were given

ultraplot_issues_tricontourf_7_1

triangulation = tri.Triangulation(x, y, conn)

fig, ax = uplot.subplot()
ax.tricontourf(triangulation, z, cmap="PuBu", levels=64, ec="none")
ax.triplot(triangulation)
---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

Cell In[29], line 4
      1 triangulation = tri.Triangulation(x, y, conn)
      3 fig, ax = uplot.subplot()
----> 4 ax.tricontourf(triangulation, z, cmap="PuBu", levels=64, ec="none")
      5 ax.triplot(triangulation)


File ~/.venv/lib/python3.11/site-packages/ultraplot/internals/inputs.py:327, in _preprocess_or_redirect.<locals>._decorator.<locals>._preprocess_or_redirect(self, *args, **kwargs)
    325     kwargs["color"] = color
    326 # Call main function
--> 327 return func(self, *args, **kwargs)


File ~/.venv/lib/python3.11/site-packages/ultraplot/axes/plot.py:4436, in PlotAxes.tricontourf(self, x, y, z, **kwargs)
   4434 kw = kwargs.copy()
   4435 if x is None or y is None or z is None:
-> 4436     raise ValueError("Three input arguments are required.")
   4437 kw.update(_pop_props(kw, "collection"))
   4438 contour_kw = _pop_kwargs(kw, "edgecolors", "linewidths", "linestyles")


ValueError: Three input arguments are required.

ultraplot_issues_tricontourf_8_1

@cvanelteren
Copy link
Contributor

Which version of MPL are you using? The docs state that two call signatures are allowed (here)

tricontourf(triangulation, z, [levels], ...)
tricontourf(x, y, z, [levels], *, [triangles=triangles], [mask=mask], ...)

PR #13 currently allows those two and I can reproduce your plots with

import ultraplot as plt, numpy as np
x = np.array([0, 1, 0, 0, 1])
y = np.array([0, 0, 1, 2, 2])
z = np.array([0, 1, -1, 0, 2])

conn = np.array([
    [0, 1, 2],
    [2, 3, 4],
])

fig, ax = plt.subplots(figsize=(4, 3))
ax.tricontourf(x, y, z, triangles = conn, levels=64, cmap="PuBu")
ax.triplot(x, y, conn, "ko-") # To display cell edges

fig, ax = plt.subplots(figsize=(4, 3))
ax.tricontourf(x, y, z, levels=64, cmap="PuBu")
ax.triplot(x, y, "ko-") # To display cell edges

image

@thibaultDrt
Copy link
Author

thibaultDrt commented Jan 8, 2025

I am using the last version of matplotlib, v3.10.0.

Actually, in the second call signature, the "triangles" optional argument can be called, leading to a third case, in a way. But it can also be called just after x and y without keyword.

For information, matplotlib handles the argument in this function here

@cvanelteren
Copy link
Contributor

Thanks for all the info! Pushed some changes in the PR that is now ready for review.

@cvanelteren
Copy link
Contributor

Fixed in #13

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