Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 0b0733b

Browse filesBrowse files
committed
Merge pull request #6764 from mdboom/fix-non-polygon-polygons2
Support returning polylines from to_polygons
1 parent 15905ce commit 0b0733b
Copy full SHA for 0b0733b

File tree

Expand file treeCollapse file tree

4 files changed

+93
-56
lines changed
Filter options
Expand file treeCollapse file tree

4 files changed

+93
-56
lines changed

‎lib/matplotlib/path.py

Copy file name to clipboardExpand all lines: lib/matplotlib/path.py
+25-9Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -583,17 +583,25 @@ def interpolated(self, steps):
583583
new_codes = None
584584
return Path(vertices, new_codes)
585585

586-
def to_polygons(self, transform=None, width=0, height=0):
586+
def to_polygons(self, transform=None, width=0, height=0, closed_only=True):
587587
"""
588-
Convert this path to a list of polygons. Each polygon is an
589-
Nx2 array of vertices. In other words, each polygon has no
590-
``MOVETO`` instructions or curves. This is useful for
591-
displaying in backends that do not support compound paths or
592-
Bezier curves, such as GDK.
588+
Convert this path to a list of polygons or polylines. Each
589+
polygon/polyline is an Nx2 array of vertices. In other words,
590+
each polygon has no ``MOVETO`` instructions or curves. This
591+
is useful for displaying in backends that do not support
592+
compound paths or Bezier curves, such as GDK.
593593
594594
If *width* and *height* are both non-zero then the lines will
595595
be simplified so that vertices outside of (0, 0), (width,
596596
height) will be clipped.
597+
598+
If *closed_only* is `True` (default), only closed polygons,
599+
with the last point being the same as the first point, will be
600+
returned. Any unclosed polylines in the path will be
601+
explicitly closed. If *closed_only* is `False`, any unclosed
602+
polygons in the path will be returned as unclosed polygons,
603+
and the closed polygons will be returned explicitly closed by
604+
setting the last point to the same as the first point.
597605
"""
598606
if len(self.vertices) == 0:
599607
return []
@@ -602,14 +610,22 @@ def to_polygons(self, transform=None, width=0, height=0):
602610
transform = transform.frozen()
603611

604612
if self.codes is None and (width == 0 or height == 0):
613+
vertices = self.vertices
614+
if closed_only:
615+
if len(vertices) < 3:
616+
return []
617+
elif np.any(vertices[0] != vertices[-1]):
618+
vertices = list(vertices) + [vertices[0]]
619+
605620
if transform is None:
606-
return [self.vertices]
621+
return [vertices]
607622
else:
608-
return [transform.transform(self.vertices)]
623+
return [transform.transform(vertices)]
609624

610625
# Deal with the case where there are curves and/or multiple
611626
# subpaths (using extension code)
612-
return _path.convert_path_to_polygons(self, transform, width, height)
627+
return _path.convert_path_to_polygons(
628+
self, transform, width, height, closed_only)
613629

614630
_unit_rectangle = None
615631

‎lib/matplotlib/tests/test_path.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_path.py
+23Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
import numpy as np
77

8+
from numpy.testing import assert_array_equal
9+
810
from matplotlib.path import Path
911
from matplotlib.patches import Polygon
1012
from nose.tools import assert_raises, assert_equal
@@ -149,6 +151,27 @@ def test_path_no_doubled_point_in_to_polygon():
149151
assert np.all(poly_clipped[-1] == poly_clipped[0])
150152

151153

154+
def test_path_to_polygons():
155+
data = [[10, 10], [20, 20]]
156+
p = Path(data)
157+
158+
assert_array_equal(p.to_polygons(width=40, height=40), [])
159+
assert_array_equal(p.to_polygons(width=40, height=40, closed_only=False),
160+
[data])
161+
assert_array_equal(p.to_polygons(), [])
162+
assert_array_equal(p.to_polygons(closed_only=False), [data])
163+
164+
data = [[10, 10], [20, 20], [30, 30]]
165+
closed_data = [[10, 10], [20, 20], [30, 30], [10, 10]]
166+
p = Path(data)
167+
168+
assert_array_equal(p.to_polygons(width=40, height=40), [closed_data])
169+
assert_array_equal(p.to_polygons(width=40, height=40, closed_only=False),
170+
[data])
171+
assert_array_equal(p.to_polygons(), [closed_data])
172+
assert_array_equal(p.to_polygons(closed_only=False), [data])
173+
174+
152175
if __name__ == '__main__':
153176
import nose
154177
nose.runmodule(argv=['-s', '--with-doctest'], exit=False)

‎src/_path.h

Copy file name to clipboardExpand all lines: src/_path.h
+29-24Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,28 @@ struct XY
4040
}
4141
};
4242

43+
typedef std::vector<XY> Polygon;
44+
45+
void _finalize_polygon(std::vector<Polygon> &result, int closed_only)
46+
{
47+
if (result.size() == 0) {
48+
return;
49+
}
50+
51+
Polygon &polygon = result.back();
52+
53+
/* Clean up the last polygon in the result. */
54+
if (polygon.size() == 0) {
55+
result.pop_back();
56+
} else if (closed_only) {
57+
if (polygon.size() < 3) {
58+
result.pop_back();
59+
} else if (polygon.front() != polygon.back()) {
60+
polygon.push_back(polygon.front());
61+
}
62+
}
63+
}
64+
4365
//
4466
// The following function was found in the Agg 2.3 examples (interactive_polygon.cpp).
4567
// It has been generalized to work on (possibly curved) polylines, rather than
@@ -509,8 +531,6 @@ bool path_in_path(PathIterator1 &a,
509531
http://en.wikipedia.org/wiki/Sutherland-Hodgman_clipping_algorithm
510532
*/
511533

512-
typedef std::vector<XY> Polygon;
513-
514534
namespace clip_to_rect_filters
515535
{
516536
/* There are four different passes needed to create/remove
@@ -696,9 +716,12 @@ clip_path_to_rect(PathIterator &path, agg::rect_d &rect, bool inside, std::vecto
696716

697717
// Empty polygons aren't very useful, so skip them
698718
if (polygon1.size()) {
719+
_finalize_polygon(results, 1);
699720
results.push_back(polygon1);
700721
}
701722
} while (code != agg::path_cmd_stop);
723+
724+
_finalize_polygon(results, 1);
702725
}
703726

704727
template <class VerticesArray, class ResultArray>
@@ -849,30 +872,12 @@ bool path_intersects_path(PathIterator1 &p1, PathIterator2 &p2)
849872
return false;
850873
}
851874

852-
void _finalize_polygon(std::vector<Polygon> &result)
853-
{
854-
Polygon &polygon = result.back();
855-
856-
if (result.size() == 0) {
857-
return;
858-
}
859-
860-
/* Clean up the last polygon in the result. If less than a
861-
triangle, remove it. */
862-
if (polygon.size() < 3) {
863-
result.pop_back();
864-
} else {
865-
if (polygon.front() != polygon.back()) {
866-
polygon.push_back(polygon.front());
867-
}
868-
}
869-
}
870-
871875
template <class PathIterator>
872876
void convert_path_to_polygons(PathIterator &path,
873877
agg::trans_affine &trans,
874878
double width,
875879
double height,
880+
int closed_only,
876881
std::vector<Polygon> &result)
877882
{
878883
typedef agg::conv_transform<py::PathIterator> transformed_path_t;
@@ -897,20 +902,20 @@ void convert_path_to_polygons(PathIterator &path,
897902

898903
while ((code = curve.vertex(&x, &y)) != agg::path_cmd_stop) {
899904
if ((code & agg::path_cmd_end_poly) == agg::path_cmd_end_poly) {
900-
_finalize_polygon(result);
905+
_finalize_polygon(result, 1);
901906
result.push_back(Polygon());
902907
polygon = &result.back();
903908
} else {
904909
if (code == agg::path_cmd_move_to) {
905-
_finalize_polygon(result);
910+
_finalize_polygon(result, closed_only);
906911
result.push_back(Polygon());
907912
polygon = &result.back();
908913
}
909914
polygon->push_back(XY(x, y));
910915
}
911916
}
912917

913-
_finalize_polygon(result);
918+
_finalize_polygon(result, closed_only);
914919
}
915920

916921
template <class VertexSource>

‎src/_path_wrapper.cpp

Copy file name to clipboardExpand all lines: src/_path_wrapper.cpp
+16-23Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,11 @@ PyObject *convert_polygon_vector(std::vector<Polygon> &polygons)
1515
npy_intp dims[2];
1616
dims[1] = 2;
1717

18-
if (poly.front() != poly.back()) {
19-
/* Make last point same as first, if not already */
20-
dims[0] = (npy_intp)poly.size() + 1;
21-
fix_endpoints = true;
22-
} else {
23-
dims[0] = (npy_intp)poly.size();
24-
fix_endpoints = false;
25-
}
18+
dims[0] = (npy_intp)poly.size();
2619

2720
numpy::array_view<double, 2> subresult(dims);
2821
memcpy(subresult.data(), &poly[0], sizeof(double) * poly.size() * 2);
2922

30-
if (fix_endpoints) {
31-
subresult(poly.size(), 0) = poly.front().x;
32-
subresult(poly.size(), 1) = poly.front().y;
33-
}
34-
3523
if (PyList_SetItem(pyresult, i, subresult.pyobj())) {
3624
Py_DECREF(pyresult);
3725
return NULL;
@@ -542,21 +530,26 @@ static PyObject *Py_convert_path_to_polygons(PyObject *self, PyObject *args, PyO
542530
py::PathIterator path;
543531
agg::trans_affine trans;
544532
double width = 0.0, height = 0.0;
533+
int closed_only = 1;
545534
std::vector<Polygon> result;
535+
const char *names[] = { "path", "transform", "width", "height", "closed_only", NULL };
546536

547-
if (!PyArg_ParseTuple(args,
548-
"O&O&|dd:convert_path_to_polygons",
549-
&convert_path,
550-
&path,
551-
&convert_trans_affine,
552-
&trans,
553-
&width,
554-
&height)) {
537+
if (!PyArg_ParseTupleAndKeywords(args,
538+
kwds,
539+
"O&O&|ddi:convert_path_to_polygons",
540+
(char **)names,
541+
&convert_path,
542+
&path,
543+
&convert_trans_affine,
544+
&trans,
545+
&width,
546+
&height,
547+
&closed_only)) {
555548
return NULL;
556549
}
557550

558551
CALL_CPP("convert_path_to_polygons",
559-
(convert_path_to_polygons(path, trans, width, height, result)));
552+
(convert_path_to_polygons(path, trans, width, height, closed_only, result)));
560553

561554
return convert_polygon_vector(result);
562555
}
@@ -827,7 +820,7 @@ extern "C" {
827820
{"affine_transform", (PyCFunction)Py_affine_transform, METH_VARARGS, Py_affine_transform__doc__},
828821
{"count_bboxes_overlapping_bbox", (PyCFunction)Py_count_bboxes_overlapping_bbox, METH_VARARGS, Py_count_bboxes_overlapping_bbox__doc__},
829822
{"path_intersects_path", (PyCFunction)Py_path_intersects_path, METH_VARARGS|METH_KEYWORDS, Py_path_intersects_path__doc__},
830-
{"convert_path_to_polygons", (PyCFunction)Py_convert_path_to_polygons, METH_VARARGS, Py_convert_path_to_polygons__doc__},
823+
{"convert_path_to_polygons", (PyCFunction)Py_convert_path_to_polygons, METH_VARARGS|METH_KEYWORDS, Py_convert_path_to_polygons__doc__},
831824
{"cleanup_path", (PyCFunction)Py_cleanup_path, METH_VARARGS, Py_cleanup_path__doc__},
832825
{"convert_to_string", (PyCFunction)Py_convert_to_string, METH_VARARGS, Py_convert_to_string__doc__},
833826
{"is_sorted", (PyCFunction)Py_is_sorted, METH_O, Py_is_sorted__doc__},

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.