Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 94 additions & 50 deletions mpldxf/backend_dxf.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,27 +35,25 @@
"""

from __future__ import absolute_import, division, print_function, unicode_literals
from io import BytesIO, StringIO
import os
import sys

import math
import os
import re
import sys
from io import StringIO

import matplotlib
import ezdxf
import numpy as np
from ezdxf.enums import TextEntityAlignment
from ezdxf.math.clipping import ClippingRect2d
from matplotlib.backend_bases import (
RendererBase,
FigureCanvasBase,
GraphicsContextBase,
FigureManagerBase,
GraphicsContextBase,
RendererBase,
)
from matplotlib.transforms import Affine2D
import matplotlib.transforms as transforms
import matplotlib.collections as mplc
import numpy as np
from shapely.geometry import LineString, Polygon
import ezdxf
from ezdxf.enums import TextEntityAlignment
from ezdxf.math.clipping import Clipping, ClippingRect2d, ConvexClippingPolygon2d

from . import dxf_colors

Expand Down Expand Up @@ -138,12 +136,8 @@ def _clip_mpl(self, gc, vertices, obj):
elif obj == "line2d":
cliprect = Polygon(cliprect)
line = LineString(vertices)
try:
intersection = line.intersection(cliprect)
except:
intersection = Polygon()
intersection = line.intersection(cliprect)

# Check if intersection is a multi-part geometry
if intersection.is_empty:
vertices = [] # No intersection
elif (
Expand All @@ -168,35 +162,86 @@ def _draw_mpl_lwpoly(self, gc, path, transform, obj):
dxfattribs = self._get_polyline_attribs(gc)
vertices = path.transformed(transform).vertices

# clip the polygon if clip rectangle present
# Check if vertices hold NaN values

if isinstance(vertices[0][0], float or np.float64):
vertices = self._clip_mpl(gc, vertices, obj=obj)
if np.isnan(vertices).any():
nan_rows = np.isnan(vertices).all(axis=1)
split_indices = np.where(nan_rows)[0]

else:
vertices = [self._clip_mpl(gc, points, obj=obj) for points in vertices]
# Split the array at NaN indices
list_of_split_vertices = np.split(vertices, split_indices)

# if vertices.
if len(vertices) == 0:
entity = None
# Remove NaN values from sub-arrays
list_of_split_vertices = [
arr[~np.isnan(arr).any(axis=1)] for arr in list_of_split_vertices
]

for split_vertices in list_of_split_vertices:
if split_vertices.size != 0:
# clip the polygon if clip rectangle present

if isinstance(split_vertices[0][0], float or np.float64):
split_vertices = self._clip_mpl(gc, split_vertices, obj=obj)

else:
split_vertices = [
self._clip_mpl(gc, points, obj=obj)
for points in split_vertices
]

# if vertices.
if len(split_vertices) == 0:
entity = None

else:
if isinstance(split_vertices[0][0], float or np.float64):
if split_vertices[0][0] != 0:
entity = self.modelspace.add_lwpolyline(
points=split_vertices,
close=False,
dxfattribs=dxfattribs,
) # set close to false because it broke some arrows
else:
entity = None

else:
entity = [
self.modelspace.add_lwpolyline(
points=points, close=False, dxfattribs=dxfattribs
)
for points in split_vertices
] # set close to false because it broke some arrows
return entity
else:
# clip the polygon if clip rectangle present

if isinstance(vertices[0][0], float or np.float64):
if vertices[0][0] != 0:
entity = self.modelspace.add_lwpolyline(
points=vertices, close=False, dxfattribs=dxfattribs
) # set close to false because it broke some arrows
else:
entity = None
vertices = self._clip_mpl(gc, vertices, obj=obj)

else:
entity = [
self.modelspace.add_lwpolyline(
points=points, close=False, dxfattribs=dxfattribs
)
for points in vertices
] # set close to false because it broke some arrows
return entity
vertices = [self._clip_mpl(gc, points, obj=obj) for points in vertices]

# if vertices.
if len(vertices) == 0:
entity = None

else:
if isinstance(vertices[0][0], float or np.float64):
if vertices[0][0] != 0:
entity = self.modelspace.add_lwpolyline(
points=vertices, close=False, dxfattribs=dxfattribs
) # set close to false because it broke some arrows
else:
entity = None

else:
entity = [
self.modelspace.add_lwpolyline(
points=points, close=False, dxfattribs=dxfattribs
)
for points in vertices
] # set close to false because it broke some arrows
return entity

def _draw_mpl_line2d(self, gc, path, transform):
line = self._draw_mpl_lwpoly(gc, path, transform, obj="line2d")
Expand Down Expand Up @@ -314,7 +359,7 @@ def draw_path_collection(
paths,
all_transforms,
offsets,
offsetTrans,
offset_trans,
facecolors,
edgecolors,
linewidths,
Expand All @@ -323,17 +368,17 @@ def draw_path_collection(
urls,
offset_position,
):
if self._groupd[-1] == "PolyCollection":
# Behandle PolyCollection som en samling av 'patch'-objekter
for path in paths:
# Kombiner master_transform med path_transform for hver path
for i, path in enumerate(paths):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Has this been reverted to an old version of the code?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason for this change?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I tried to figure out where the failure happend, I tried some different approaches and that we didnt handle all_transforms was one thing I was looking into.
I just revert it for now, even though I think the new code is working asmit should as well :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I figured out it actually fixed a bug as well!
I completely forgot, but some lines from the simple sounding plot was not plotted before this change.
So I will actually leave it as it is here :)

if len(all_transforms):
combined_transform = master_transform + all_transforms[i]
else:
combined_transform = master_transform
# Her kan du velge å bruke eller tilpasse rgbFace basert på facecolors, hvis det er relevant
if facecolors.size:
rgbFace = facecolors[0] if facecolors is not None else None
else:
rgbFace = None
self._draw_mpl_patch(gc, path, combined_transform, rgbFace)
if facecolors.size:
rgbFace = facecolors[0] if facecolors is not None else None
else:
rgbFace = None
# Draw each path as a filled patch
self._draw_mpl_patch(gc, path, combined_transform, rgbFace=rgbFace)

def draw_path(self, gc, path, transform, rgbFace=None):
# print('\nEntered ###DRAW_PATH###')
Expand All @@ -352,7 +397,6 @@ def draw_path(self, gc, path, transform, rgbFace=None):
elif self._groupd[-1] == "line2d":
line = self._draw_mpl_line2d(gc, path, transform)

# Note if this is used then tick marks and lines with markers go through this function
def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None):
# print('\nEntered ###DRAW_MARKERS###')
# print('\t', self._groupd)
Expand Down
Binary file added tests/files/test_boxplot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/files/test_contour.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/files/test_contourf.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/files/test_plot_line.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/files/test_plot_line_with_no_axis.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/files/test_plot_with_data_outside_axes.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/files/test_plot_with_nans.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
129 changes: 112 additions & 17 deletions tests/test_backend_ezdxf.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,38 +27,94 @@
from matplotlib import pyplot as plt
import numpy as np
from numpy.random import random
import pytest

from mpldxf import backend_dxf


matplotlib.backend_bases.register_backend("dxf", backend_dxf.FigureCanvas)


class DxfBackendTestCase(unittest.TestCase):
class TestDxfBackendCase(unittest.TestCase):
"""Tests for the dxf backend."""

def test_plot_line_with_no_axis(self):
"""Test a simple line-plot command."""
plt.gca().patch.set_visible(False)
plt.plot(range(5), [1, 2, 3, 2, 4])
plt.plot(range(7), [1, 2, 3, 2, 4, 6, 7])
plt.axis("off")
plt.savefig("tests/files/test_plot_line_with_no_axis.png")

outfile = "tests/files/test_plot_line_with_no_axis.dxf"
plt.close()
try:
outfile = "tests/files/test_plot_line_with_no_axis.dxf"
plt.savefig(outfile, transparent=True)
finally:
plt.close()

# Load the DXF file and inspect its content
doc = ezdxf.readfile(outfile)
modelspace = doc.modelspace()
entities = list(modelspace)
assert len(entities) == 2 # 1 line and the bounding box of the plot
assert len(entities) == 1 # 1 line and the bounding box of the plot

def test_plot_line(self):
"""Test a simple line-plot command."""
plt.gca().patch.set_visible(False)
plt.plot(range(3), [1, 2, 3])
outfile = "tests/files/test_plot_line.dxf"
plt.savefig(outfile, transparent=True)
plt.close()
plt.savefig("tests/files/test_plot_line.png")

try:
outfile = "tests/files/test_plot_line.dxf"
plt.savefig(outfile, transparent=True)
finally:
plt.close()

# Load the DXF file and inspect its content
doc = ezdxf.readfile(outfile)
modelspace = doc.modelspace()
entities = list(modelspace)
entity_types = set([entity.dxftype() for entity in entities])
assert entity_types == {"LWPOLYLINE", "TEXT"}

def test_plot_with_data_outside_axes(self):
"""Test a simple line-plot command with data outside the axes."""
plt.plot(range(7), [1, 2, 3, 1e5, 5, 6, 7])
plt.ylim(0, 7)
plt.xlim(1, 6)
plt.savefig("tests/files/test_plot_with_data_outside_axes.png")

try:
plt.savefig("tests/files/test_plot_with_data_outside_axes.png")
outfile = "tests/files/test_plot_with_data_outside_axes.dxf"
plt.savefig(outfile, transparent=True)
finally:
plt.close()

# Load the DXF file and inspect its content
doc = ezdxf.readfile(outfile)
modelspace = doc.modelspace()
entities = list(modelspace)
entity_types = set([entity.dxftype() for entity in entities])
assert entity_types == {"LWPOLYLINE", "TEXT"}

def test_plot_with_twin_axis_and_data_outside_axes(self):
"""Test a simple line-plot command with data outside the axes."""
fig, ax1 = plt.subplots()
ax2 = ax1.twinx()
ax1.plot(range(7), [1, 2, 3, 1e5, 5, 6, 7])
ax2.plot(range(7), [1, 2, 3, 1e5, 5, 6, 7])
ax1.set_ylim(1, 6)
ax2.set_ylim(1, 6)
plt.savefig("tests/files/test_plot_with_twin_axis_and_data_outside_axes.png")

try:
plt.savefig(
"tests/files/test_plot_with_twin_axis_and_data_outside_axes.png"
)
outfile = "tests/files/test_plot_with_twin_axis_and_data_outside_axes.dxf"
plt.savefig(outfile, transparent=True)
finally:
plt.close()

# Load the DXF file and inspect its content
doc = ezdxf.readfile(outfile)
Expand All @@ -76,20 +132,29 @@ def test_boxplot(self):
[3, 5, 6, 7, 9, 10, 12, 13],
]
plt.boxplot(data)
outfile = "tests/files/test_boxplot.dxf"
plt.savefig(outfile)
plt.close()
plt.savefig("tests/files/test_boxplot.png")

try:
outfile = "tests/files/test_boxplot.dxf"
plt.savefig(outfile)
finally:
plt.close()

def test_contour(self):
"""Test some contours."""
print("TEST CONTOUR")
x = np.linspace(-5.0, 5.0, 30)
y = np.linspace(-5.0, 5.0, 30)
X, Y = np.meshgrid(x, y)
Z = np.sin(np.sqrt(X**2 + Y**2))
plt.contour(X, Y, Z)
outfile = "tests/files/test_contour.dxf"
plt.savefig(outfile)
plt.close()
plt.savefig("tests/files/test_contour.png")

try:
outfile = "tests/files/test_contour.dxf"
plt.savefig(outfile)
finally:
plt.close()

def test_contourf(self):
"""Test some filled contours."""
Expand All @@ -98,6 +163,36 @@ def test_contourf(self):
X, Y = np.meshgrid(x, y)
Z = np.sin(np.sqrt(X**2 + Y**2))
plt.contourf(X, Y, Z)
outfile = "tests/files/test_contourf.dxf"
plt.savefig(outfile)
plt.close()

plt.savefig("tests/files/test_contourf.png")

try:
outfile = "tests/files/test_contourf.dxf"
plt.savefig(outfile)

finally:
plt.close()

def test_plot_with_nans(self):
"""Test a plot with NaNs."""
plt.gca().patch.set_visible(False)
x = [1, 2, 3, 4, 5, 6]
y = [1, 2, 3, np.nan, 5, 6]
plt.plot(x, y)
plt.axis("off")

plt.savefig("tests/files/test_plot_with_nans.png")

try:
outfile = "tests/files/test_plot_with_nans.dxf"
plt.savefig(outfile)
finally:
plt.close()

# Load the DXF file and inspect its content
doc = ezdxf.readfile(outfile)
modelspace = doc.modelspace()
entities = list(modelspace)
assert (
len(entities) == 2
) # ideally we should have two lines (i.e. one broken line), but one interpolated line works as a hotfix
Loading