During computation of tight_layout of subplots containing a histogram one gets a ValueError in bezier.py:35: Given lines do not intersect. Please verify that the angles are not equal or differ by 180 degrees.
importmatplotlib.pyplotaspltimportnumpyasnpdefplot_1D_histogram_trajectories(data, centers=None,dtrajs=None, max_traj_length=200, ax=None):
forn, _trajinenumerate(data):
ax.hist(_traj, bins=30, alpha=.33, density=True, color='C{}'.format(n))
ylims=ax.get_ylim()
xlims=ax.get_xlim()
forn, _trajinenumerate(data):
ax.plot(
_traj[:min(len(_traj), max_traj_length)],
np.linspace(*ylims, min(len(_traj), max_traj_length)),
alpha=0.6, color='C{}'.format(n), label='traj {}'.format(n))
ax.plot(
centers[dtrajs[n][:min(len(_traj), max_traj_length)]],
np.linspace(*ylims, min(len(_traj), max_traj_length)),
'.-', alpha=.6, label='dtraj {}'.format(n), linewidth=.3)
ax.annotate(
'', xy=(0.85*xlims[1], 0.7*ylims[1]), xytext=(0.85*xlims[1], 0.3*ylims[1]),
# FIXME: if one omits this argument, it succeedsarrowprops=dict(),
)
ax.text(0.86*xlims[1], 0.5*ylims[1], '$x(time)$', ha='left', va='center', rotation=90)
ax.legend(loc=2)
# FIXME: if only one subfigure is created it succeedsfig, axes=plt.subplots(1, 2, figsize=(10, 4))
data= [np.loadtxt('data.csv')]
dtraj=np.loadtxt('dtraj.csv').astype(int)
centers=np.loadtxt('centers.csv')
plot_1D_histogram_trajectories(data, centers=centers, dtrajs=[dtraj], ax=axes[0])
axes[1].plot(x=np.arange(10), y=np.arange(10))
fig.tight_layout()
leads to
File "/home/mi/marscher/sources/pyemma_tutorials/notebooks/minimal_example.py", line 34, in <module>
fig.tight_layout()
File "/srv/public/marscher/miniconda3/envs/nbtut/lib/python3.6/site-packages/matplotlib/figure.py", line 2374, in tight_layout
pad=pad, h_pad=h_pad, w_pad=w_pad, rect=rect)
File "/srv/public/marscher/miniconda3/envs/nbtut/lib/python3.6/site-packages/matplotlib/tight_layout.py", line 366, in get_tight_layout_figure
pad=pad, h_pad=h_pad, w_pad=w_pad)
File "/srv/public/marscher/miniconda3/envs/nbtut/lib/python3.6/site-packages/matplotlib/tight_layout.py", line 115, in auto_adjust_subplotpars
tight_bbox_raw = union([ax.get_tightbbox(renderer) for ax in subplots
File "/srv/public/marscher/miniconda3/envs/nbtut/lib/python3.6/site-packages/matplotlib/tight_layout.py", line 116, in <listcomp>
if ax.get_visible()])
File "/srv/public/marscher/miniconda3/envs/nbtut/lib/python3.6/site-packages/matplotlib/axes/_base.py", line 4396, in get_tightbbox
bbox = a.get_tightbbox(renderer)
File "/srv/public/marscher/miniconda3/envs/nbtut/lib/python3.6/site-packages/matplotlib/artist.py", line 271, in get_tightbbox
bbox = self.get_window_extent(renderer)
File "/srv/public/marscher/miniconda3/envs/nbtut/lib/python3.6/site-packages/matplotlib/text.py", line 2425, in get_window_extent
bboxes.append(self.arrow_patch.get_window_extent())
File "/srv/public/marscher/miniconda3/envs/nbtut/lib/python3.6/site-packages/matplotlib/patches.py", line 548, in get_window_extent
return self.get_path().get_extents(self.get_transform())
File "/srv/public/marscher/miniconda3/envs/nbtut/lib/python3.6/site-packages/matplotlib/patches.py", line 4228, in get_path
_path, fillable = self.get_path_in_displaycoord()
File "/srv/public/marscher/miniconda3/envs/nbtut/lib/python3.6/site-packages/matplotlib/patches.py", line 4258, in get_path_in_displaycoord
self.get_mutation_aspect())
File "/srv/public/marscher/miniconda3/envs/nbtut/lib/python3.6/site-packages/matplotlib/patches.py", line 3218, in __call__
return self.transmute(path, mutation_size, linewidth)
File "/srv/public/marscher/miniconda3/envs/nbtut/lib/python3.6/site-packages/matplotlib/patches.py", line 3718, in transmute
tail_width / 2.)
File "/srv/public/marscher/miniconda3/envs/nbtut/lib/python3.6/site-packages/matplotlib/bezier.py", line 381, in get_parallels
cos_t2, sin_t2)
File "/srv/public/marscher/miniconda3/envs/nbtut/lib/python3.6/site-packages/matplotlib/bezier.py", line 35, in get_intersection
raise ValueError("Given lines do not intersect. Please verify that "
ValueError: Given lines do not intersect. Please verify that the angles are not equal or differ by 180 degrees.
Expected outcome
It works, if the arrowprops are omitted, which means not to draw an arrow.
changed the title [-]ValueError: lines do not intersect when drawing arrow with filled paths[/-][+]ValueError: lines do not intersect when computing tight bounding box containing arrow with filled paths[/+]on Nov 16, 2018
changed the title [-]ValueError: lines do not intersect when computing tight bounding box containing arrow with filled paths[/-][+][Annotations] ValueError: lines do not intersect when computing tight bounding box containing arrow with filled paths[/+]on Nov 16, 2018
I would like to report that I got this error also when doing animation with matplotlib and this particular error did not occur in 2017. I only ran into it after updating matplotlib. I found that I would not run into this error if I disable a certain line in the animation.
Also found out that if I downgrade to ver 2.3.3, then it works.
I also would like to report getting this error. I am on Matplotlib 3.1.3 I will note that by switching the backend to TkAggmatplotlib.use('TkAgg') I was able to save the figure as desired.
Okay, so it is backend dependent (I have not done an exhaustive search, but there is something weird going on, that I cannot replicate all behavior in a clean venv)
So (@darienmorrow uses conda on his main machine, for what it is worth, unless otherwise noted, packages are from conda-forge):
In his base venv, the figure fails to build under Agg and Qt5Agg, but succeeds under TkAgg
His base venv is python 3.6.7, mpl 3.1.3, installed via conda-forge.
All other configurations I have tried fail only under Qt5Agg (having tested Agg, Qt5Agg, TkAgg).
This includes clean venv installs only, but including Qt (installed the conda forge matplotlib full package, not just matplotlib-base):
Python 3.8, mpl 3.1.3
Python 3.6.7, mpl 3.1.3 (would have thought this to be functionally identical to the original venv, but something isn't the same, not sure what)
Python 3.8, mpl 3.2.0rc2 (from pip, after first installing 3.1.3 from conda-forge, to maintain all dependencies as identical)
Python 3.8, matplotlib freshly pulled from master (dc1f0d9) (On my own machine, not conda, just did more testing, was able to actually reproduce, I hadn't zeroed in on Qt5Agg as the major culprit yet, did not have qt bindings installed yet)
Unfortunately, this figure is burried in a fairly long script, and attempts to break out only the failing parts have not triggered the error. I may have another go at it, though.
It is very sensitive to the hspace/wspace number (13 sig figs, any larger perturbation, the error disappears). I just made our create_figure function print out what it actually passed to gridspec when it was failing.
It did NOT fail if I did not use inset_axes, and just used the whole axis from that subplot (I do not think this is due to the fact that they are inset, just that it changes the positions and proportions to be just right, the way it is proportioned)
It does NOT fail if I omit the subplots_adjust call
Both annotate calls are needed to trigger the error
Many of the numbers (such as x and y positions, figure size) are also sensative, but I have not quatitated how sensitive (just added e.g. a 1 after the least significant digit)
Does NOT fail if I take "headlength" out of arrowprops, continues to fail if the other params are removed
Also, the figure script is why I opened up #15049, just noticed that we are still explicitly passing s here... @darienmorrow, may be a good idea to just pass that in as a positional arg, since the name is changing
Yikes, thanks for running this to ground @ksunden !
From a very cursory investigation it looks like we have one part of the code that is checking "are you parallel" using one threshold and then calling code that checks "are you not parallel" using a slightly different threshold.
Per @tacaswell's comment, the following patch appears to fix the issue:
diff --git i/lib/matplotlib/bezier.py w/lib/matplotlib/bezier.py
index 9e347ce87..8cec64f15 100644
--- i/lib/matplotlib/bezier.py+++ w/lib/matplotlib/bezier.py@@ -389,23 +389,23 @@ def get_parallels(bezier2, width):
# find cm_left which is the intersecting point of a line through
# c1_left with angle t1 and a line through c2_left with angle
# t2. Same with cm_right.
- if parallel_test != 0:- # a special case for a straight line, i.e., angle between two- # lines are smaller than some (arbitrary) value.- cmx_left, cmy_left = (- 0.5 * (c1x_left + c2x_left), 0.5 * (c1y_left + c2y_left)- )- cmx_right, cmy_right = (- 0.5 * (c1x_right + c2x_right), 0.5 * (c1y_right + c2y_right)- )- else:+ try:
cmx_left, cmy_left = get_intersection(c1x_left, c1y_left, cos_t1,
sin_t1, c2x_left, c2y_left,
cos_t2, sin_t2)
-
cmx_right, cmy_right = get_intersection(c1x_right, c1y_right, cos_t1,
sin_t1, c2x_right, c2y_right,
cos_t2, sin_t2)
+ except ValueError:+ # Special case straight lines, i.e., angle between two lines is+ # less than the threshold used by get_intersection (we don't use+ # check_if_parallel as the threshold is not the same).+ cmx_left, cmy_left = (+ 0.5 * (c1x_left + c2x_left), 0.5 * (c1y_left + c2y_left)+ )+ cmx_right, cmy_right = (+ 0.5 * (c1x_right + c2x_right), 0.5 * (c1y_right + c2y_right)+ )
# the parallel Bezier lines are created with control points of
# [c1_left, cm_left, c2_left] and [c1_right, cm_right, c2_right]
That is a test which fails (raises ValueError) on current master (8ac3ea1), but passes with the above patch applied.
We could add checks that it gives expected results, but really all that we are looking for is that it doesn't error out. Unless I am mistaken, there are no unit tests targeting only bezier.py (though it is called often in the rest of the test suite)
I don't think we should worry about such a case; clearly we should be able to pick whatever threshold for parallel-checking makes sense for us (in a sense get_parallels should be private API).
Activity
marscher commented on Nov 16, 2018
fyi @thempel
anntzer commented on Nov 16, 2018
bisects to #11801.
marscher commented on Nov 16, 2018
that was quick!
ImportanceOfBeingErnest commented on Nov 16, 2018
Since I have currently no idea at which point in the code this needs to be fixed, a workaround is to not use parallel lines,
ax.annotate('', xy=(0.8500001 * xlims[1], 0.7 * ylims[1]), xytext=(0.85 * xlims[1], 0.3 * ylims[1]), arrowprops=dict(), )[08] circumvent matplotlib bug
iruletheworld commented on Feb 28, 2019
I would like to report that I got this error also when doing animation with matplotlib and this particular error did not occur in 2017. I only ran into it after updating matplotlib. I found that I would not run into this error if I disable a certain line in the animation.
Also found out that if I downgrade to ver 2.3.3, then it works.
darienmorrow commented on Feb 25, 2020
I also would like to report getting this error. I am on Matplotlib 3.1.3 I will note that by switching the backend to TkAggmatplotlib.use('TkAgg') I was able to save the figure as desired.
tacaswell commented on Feb 25, 2020
@darienmorrow do you have a more minimal example that reproduces this?
ksunden commented on Feb 25, 2020
I was working with @darienmorrow on his figure, I was unable to reproduce on current master (9895288)
tacaswell commented on Feb 25, 2020
Can you try with the v3.2.0 rc (pip install --pre -U matplotlib )?
ksunden commented on Feb 25, 2020
Okay, so it is backend dependent (I have not done an exhaustive search, but there is something weird going on, that I cannot replicate all behavior in a clean venv)
So (@darienmorrow uses conda on his main machine, for what it is worth, unless otherwise noted, packages are from conda-forge):
In his base venv, the figure fails to build under Agg and Qt5Agg, but succeeds under TkAgg
His base venv is python 3.6.7, mpl 3.1.3, installed via conda-forge.
All other configurations I have tried fail only under Qt5Agg (having tested Agg, Qt5Agg, TkAgg).
This includes clean venv installs only, but including Qt (installed the conda forge matplotlib full package, not just matplotlib-base):
Python 3.8, mpl 3.1.3
Python 3.6.7, mpl 3.1.3 (would have thought this to be functionally identical to the original venv, but something isn't the same, not sure what)
Python 3.8, mpl 3.2.0rc2 (from pip, after first installing 3.1.3 from conda-forge, to maintain all dependencies as identical)
Python 3.8, matplotlib freshly pulled from master (dc1f0d9) (On my own machine, not conda, just did more testing, was able to actually reproduce, I hadn't zeroed in on Qt5Agg as the major culprit yet, did not have qt bindings installed yet)
Unfortunately, this figure is burried in a fairly long script, and attempts to break out only the failing parts have not triggered the error. I may have another go at it, though.
ksunden commented on Feb 26, 2020
Okay, I have a simplified script. It seems to be a very unlucky rounding condition.
Some notes:
Also, the figure script is why I opened up #15049, just noticed that we are still explicitly passing s here... @darienmorrow, may be a good idea to just pass that in as a positional arg, since the name is changing
tacaswell commented on Feb 26, 2020
Yikes, thanks for running this to ground @ksunden !
From a very cursory investigation it looks like we have one part of the code that is checking "are you parallel" using one threshold and then calling code that checks "are you not parallel" using a slightly different threshold.
anntzer commented on Feb 26, 2020
Per @tacaswell's comment, the following patch appears to fix the issue:
Just needs a test, yada yada.
ksunden commented on Feb 26, 2020
def test_almost_parallel(): matplotlib.bezier.get_parallels([[1, 1],[1, 2],[1 + 1e-14, 3 ]], 1)That is a test which fails (raises ValueError) on current master (8ac3ea1), but passes with the above patch applied.
We could add checks that it gives expected results, but really all that we are looking for is that it doesn't error out. Unless I am mistaken, there are no unit tests targeting only bezier.py (though it is called often in the rest of the test suite)
anntzer commented on Feb 27, 2020
I don't think we should worry about such a case; clearly we should be able to pick whatever threshold for parallel-checking makes sense for us (in a sense get_parallels should be private API).
tacaswell commented on Feb 27, 2020
I agree with @anntzer . We don't usually consider tweaking floating point thresholds an API change.
ksunden commented on Feb 27, 2020
Fair enough, then it is unclear to me that there are any actual tests required in this instance.
Avoid floating point rounding causing bezier.get_parallels to fail