Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a274933

Browse files
authored
Merge pull request #6764 from mdboom/fix-non-polygon-polygons2
Support returning polylines from to_polygons
2 parents 2f2b08d + d02b01d commit a274933

File tree

4 files changed

+93
-56
lines changed

4 files changed

+93
-56
lines changed

lib/matplotlib/path.py

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -582,17 +582,25 @@ def interpolated(self, steps):
582582
new_codes = None
583583
return Path(vertices, new_codes)
584584

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

603611
if self.codes is None and (width == 0 or height == 0):
612+
vertices = self.vertices
613+
if closed_only:
614+
if len(vertices) < 3:
615+
return []
616+
elif np.any(vertices[0] != vertices[-1]):
617+
vertices = list(vertices) + [vertices[0]]
618+
604619
if transform is None:
605-
return [self.vertices]
620+
return [vertices]
606621
else:
607-
return [transform.transform(self.vertices)]
622+
return [transform.transform(vertices)]
608623

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

613629
_unit_rectangle = None
614630

lib/matplotlib/tests/test_path.py

Lines 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

Lines 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

Lines 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)