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

Skip to content

Commit bd77bf2

Browse files
committed
Take stroke width into account when quantizing rectilinear paths. Discovered by Jason Grout in the mailing list thread "Plots shifted up or to the left a pixel or so"
svn path=/trunk/matplotlib/; revision=8414
1 parent f0c0647 commit bd77bf2

9 files changed

Lines changed: 63 additions & 22 deletions

File tree

lib/matplotlib/artist.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ def get_snap(self):
412412
* None: (auto) If the path contains only rectilinear line
413413
segments, round to the nearest pixel center
414414
415-
Only supported by the Agg backends.
415+
Only supported by the Agg and MacOSX backends.
416416
"""
417417
return self._snap
418418

@@ -427,7 +427,7 @@ def set_snap(self, snap):
427427
* None: (auto) If the path contains only rectilinear line
428428
segments, round to the nearest pixel center
429429
430-
Only supported by the Agg backends.
430+
Only supported by the Agg and MacOSX backends.
431431
"""
432432
self._snap = snap
433433

lib/matplotlib/path.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,8 @@ def __len__(self):
188188
return len(self.vertices)
189189

190190
def iter_segments(self, transform=None, remove_nans=True, clip=None,
191-
quantize=False, simplify=None, curves=True):
191+
quantize=False, stroke_width=1.0, simplify=None,
192+
curves=True):
192193
"""
193194
Iterates over all of the curve segments in the path. Each
194195
iteration returns a 2-tuple (*vertices*, *code*), where
@@ -210,6 +211,9 @@ def iter_segments(self, transform=None, remove_nans=True, clip=None,
210211
*quantize*: if None, auto-quantize. If True, force quantize,
211212
and if False, don't quantize.
212213
214+
*stroke_width*: the width of the stroke being drawn. Needed
215+
as a hint for the quantizer.
216+
213217
*simplify*: if True, perform simplification, to remove
214218
vertices that do not affect the appearance of the path. If
215219
False, perform no simplification. If None, use the
@@ -232,7 +236,7 @@ def iter_segments(self, transform=None, remove_nans=True, clip=None,
232236
STOP = self.STOP
233237

234238
vertices, codes = cleanup_path(self, transform, remove_nans, clip,
235-
quantize, simplify, curves)
239+
quantize, stroke_width, simplify, curves)
236240
len_vertices = len(vertices)
237241

238242
i = 0
3 Bytes
Loading
19 Bytes
Loading

src/_backend_agg.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -535,14 +535,16 @@ RendererAgg::draw_markers(const Py::Tuple& args) {
535535
transformed_path_t marker_path_transformed(marker_path, marker_trans);
536536
quantize_t marker_path_quantized(marker_path_transformed,
537537
gc.quantize_mode,
538-
marker_path.total_vertices());
538+
marker_path.total_vertices(),
539+
gc.linewidth);
539540
curve_t marker_path_curve(marker_path_quantized);
540541

541542
PathIterator path(path_obj);
542543
transformed_path_t path_transformed(path, trans);
543544
quantize_t path_quantized(path_transformed,
544545
gc.quantize_mode,
545-
path.total_vertices());
546+
path.total_vertices(),
547+
1.0);
546548
curve_t path_curve(path_quantized);
547549
path_curve.rewind(0);
548550

@@ -1106,7 +1108,7 @@ RendererAgg::draw_path(const Py::Tuple& args) {
11061108
transformed_path_t tpath(path, trans);
11071109
nan_removed_t nan_removed(tpath, true, path.has_curves());
11081110
clipped_t clipped(nan_removed, clip, width, height);
1109-
quantized_t quantized(clipped, gc.quantize_mode, path.total_vertices());
1111+
quantized_t quantized(clipped, gc.quantize_mode, path.total_vertices(), gc.linewidth);
11101112
simplify_t simplified(quantized, simplify, path.simplify_threshold());
11111113
curve_t curve(simplified);
11121114

@@ -1273,7 +1275,8 @@ RendererAgg::_draw_path_collection_generic
12731275
transformed_path_t tpath(path, trans);
12741276
nan_removed_t nan_removed(tpath, true, has_curves);
12751277
clipped_t clipped(nan_removed, do_clip, width, height);
1276-
quantized_t quantized(clipped, gc.quantize_mode, path.total_vertices());
1278+
quantized_t quantized(clipped, gc.quantize_mode,
1279+
path.total_vertices(), gc.linewidth);
12771280
if (has_curves) {
12781281
quantized_curve_t curve(quantized);
12791282
_draw_path(curve, has_clippath, face, gc);

src/_macosx.m

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ static void _draw_hatch(void *info, CGContextRef cr)
289289
0,
290290
rect,
291291
QUANTIZE_FALSE,
292+
1.0,
292293
0);
293294
Py_DECREF(transform);
294295
if (!iterator)
@@ -662,6 +663,7 @@ static int _get_snap(GraphicsContext* self, enum e_quantize_mode* mode)
662663
0,
663664
rect,
664665
QUANTIZE_AUTO,
666+
1.0,
665667
0);
666668
Py_DECREF(transform);
667669
if (!iterator)
@@ -889,6 +891,7 @@ static int _get_snap(GraphicsContext* self, enum e_quantize_mode* mode)
889891
0,
890892
rect,
891893
QUANTIZE_AUTO,
894+
CGContextGetLineWidth(self),
892895
rgbFace == NULL);
893896
if (!iterator)
894897
{
@@ -966,6 +969,7 @@ static int _get_snap(GraphicsContext* self, enum e_quantize_mode* mode)
966969
0,
967970
rect,
968971
QUANTIZE_AUTO,
972+
CGContextGetLineWidth(self),
969973
0);
970974
if (!iterator)
971975
{
@@ -1042,6 +1046,7 @@ static int _get_snap(GraphicsContext* self, enum e_quantize_mode* mode)
10421046
0,
10431047
rect,
10441048
mode,
1049+
CGContextGetLineWidth(self),
10451050
0);
10461051
if (!iterator)
10471052
{
@@ -1063,6 +1068,7 @@ static int _get_snap(GraphicsContext* self, enum e_quantize_mode* mode)
10631068
1,
10641069
rect,
10651070
QUANTIZE_TRUE,
1071+
1.0,
10661072
0);
10671073
if (!iterator)
10681074
{
@@ -1326,6 +1332,17 @@ static BOOL _clip(CGContextRef cr, PyObject* object)
13261332
0,
13271333
rect,
13281334
mode,
1335+
1.0,
1336+
/* Hardcoding stroke width to 1.0
1337+
here, but for true
1338+
correctness, the paths would
1339+
need to be set up for each
1340+
different linewidth that may
1341+
be applied below. This
1342+
difference is very minute in
1343+
practice, so this hardcoding
1344+
is probably ok for now. --
1345+
MGD */
13291346
0);
13301347
Py_DECREF(transform);
13311348
Py_DECREF(path);
@@ -1362,6 +1379,7 @@ static BOOL _clip(CGContextRef cr, PyObject* object)
13621379
0,
13631380
rect,
13641381
QUANTIZE_AUTO,
1382+
1.0,
13651383
0);
13661384
if (!iterator)
13671385
{
@@ -1669,6 +1687,7 @@ static BOOL _clip(CGContextRef cr, PyObject* object)
16691687
0,
16701688
rect,
16711689
QUANTIZE_AUTO,
1690+
1.0,
16721691
0);
16731692
if (iterator)
16741693
{
@@ -2654,6 +2673,7 @@ static void _data_provider_release(void* info, const void* data, size_t size)
26542673
0,
26552674
rect,
26562675
QUANTIZE_AUTO,
2676+
1.0,
26572677
0);
26582678
if (iterator)
26592679
{

src/_path.cpp

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1228,8 +1228,9 @@ void __cleanup_path(VertexSource& source,
12281228
void _cleanup_path(PathIterator& path, const agg::trans_affine& trans,
12291229
bool remove_nans, bool do_clip,
12301230
const agg::rect_base<double>& rect,
1231-
e_quantize_mode quantize_mode, bool do_simplify,
1232-
bool return_curves, std::vector<double>& vertices,
1231+
e_quantize_mode quantize_mode, double stroke_width,
1232+
bool do_simplify, bool return_curves,
1233+
std::vector<double>& vertices,
12331234
std::vector<npy_uint8>& codes) {
12341235
typedef agg::conv_transform<PathIterator> transformed_path_t;
12351236
typedef PathNanRemover<transformed_path_t> nan_removal_t;
@@ -1241,7 +1242,7 @@ void _cleanup_path(PathIterator& path, const agg::trans_affine& trans,
12411242
transformed_path_t tpath(path, trans);
12421243
nan_removal_t nan_removed(tpath, remove_nans, path.has_curves());
12431244
clipped_t clipped(nan_removed, do_clip, rect);
1244-
quantized_t quantized(clipped, quantize_mode, path.total_vertices());
1245+
quantized_t quantized(clipped, quantize_mode, path.total_vertices(), stroke_width);
12451246
simplify_t simplified(quantized, do_simplify, path.simplify_threshold());
12461247

12471248
vertices.reserve(path.total_vertices() * 2);
@@ -1260,7 +1261,7 @@ void _cleanup_path(PathIterator& path, const agg::trans_affine& trans,
12601261

12611262
Py::Object _path_module::cleanup_path(const Py::Tuple& args)
12621263
{
1263-
args.verify_length(7);
1264+
args.verify_length(8);
12641265

12651266
PathIterator path(args[0]);
12661267
agg::trans_affine trans = py_to_agg_transformation_matrix(args[1].ptr(), false);
@@ -1300,8 +1301,10 @@ Py::Object _path_module::cleanup_path(const Py::Tuple& args)
13001301
quantize_mode = QUANTIZE_FALSE;
13011302
}
13021303

1304+
double stroke_width = Py::Float(args[5]);
1305+
13031306
bool simplify;
1304-
Py::Object simplify_obj = args[5];
1307+
Py::Object simplify_obj = args[6];
13051308
if (simplify_obj.isNone())
13061309
{
13071310
simplify = path.should_simplify();
@@ -1311,13 +1314,13 @@ Py::Object _path_module::cleanup_path(const Py::Tuple& args)
13111314
simplify = simplify_obj.isTrue();
13121315
}
13131316

1314-
bool return_curves = args[6].isTrue();
1317+
bool return_curves = args[7].isTrue();
13151318

13161319
std::vector<double> vertices;
13171320
std::vector<npy_uint8> codes;
13181321

13191322
_cleanup_path(path, trans, remove_nans, do_clip, clip_rect, quantize_mode,
1320-
simplify, return_curves, vertices, codes);
1323+
stroke_width, simplify, return_curves, vertices, codes);
13211324

13221325
npy_intp length = codes.size();
13231326
npy_intp dims[] = { length, 2, 0 };

src/path_cleanup.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,16 @@ class PathCleanupIterator
2828
PathCleanupIterator(PyObject* path, agg::trans_affine trans,
2929
bool remove_nans, bool do_clip,
3030
const agg::rect_base<double>& rect,
31-
e_quantize_mode quantize_mode, bool do_simplify) :
31+
e_quantize_mode quantize_mode, double stroke_width,
32+
bool do_simplify) :
3233
m_path_obj(path, true),
3334
m_path_iter(m_path_obj),
3435
m_transform(trans),
3536
m_transformed(m_path_iter, m_transform),
3637
m_nan_removed(m_transformed, remove_nans, m_path_iter.has_curves()),
3738
m_clipped(m_nan_removed, do_clip, rect),
38-
m_quantized(m_clipped, quantize_mode, m_path_iter.total_vertices()),
39+
m_quantized(m_clipped, quantize_mode, m_path_iter.total_vertices(),
40+
stroke_width),
3941
m_simplify(m_quantized, do_simplify && m_path_iter.should_simplify(),
4042
m_path_iter.simplify_threshold())
4143
{
@@ -53,14 +55,15 @@ extern "C" {
5355
void*
5456
get_path_iterator(
5557
PyObject* path, PyObject* trans, int remove_nans, int do_clip,
56-
double rect[4], e_quantize_mode quantize_mode, int do_simplify)
58+
double rect[4], e_quantize_mode quantize_mode, double stroke_width,
59+
int do_simplify)
5760
{
5861
agg::trans_affine agg_trans = py_to_agg_transformation_matrix(trans, false);
5962
agg::rect_base<double> clip_rect(rect[0], rect[1], rect[2], rect[3]);
6063

6164
PathCleanupIterator* pipeline = new PathCleanupIterator(
6265
path, agg_trans, remove_nans != 0, do_clip != 0,
63-
clip_rect, quantize_mode, do_simplify != 0);
66+
clip_rect, quantize_mode, stroke_width, do_simplify != 0);
6467

6568
return (void*)pipeline;
6669
}

src/path_converters.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,7 @@ class PathQuantizer
378378
private:
379379
VertexSource* m_source;
380380
bool m_quantize;
381+
double m_quantize_value;
381382

382383
static bool should_quantize(VertexSource& path,
383384
e_quantize_mode quantize_mode,
@@ -436,10 +437,17 @@ class PathQuantizer
436437
- QUANTIZE_FALSE: No quantization
437438
*/
438439
PathQuantizer(VertexSource& source, e_quantize_mode quantize_mode,
439-
unsigned total_vertices=15) :
440+
unsigned total_vertices=15, double stroke_width=0.0) :
440441
m_source(&source)
441442
{
442443
m_quantize = should_quantize(source, quantize_mode, total_vertices);
444+
445+
if (m_quantize)
446+
{
447+
int odd_even = (int)mpl_round(stroke_width) % 2;
448+
m_quantize_value = (odd_even) ? 0.5 : 0.0;
449+
}
450+
443451
source.rewind(0);
444452
}
445453

@@ -454,8 +462,8 @@ class PathQuantizer
454462
code = m_source->vertex(x, y);
455463
if (m_quantize && agg::is_vertex(code))
456464
{
457-
*x = mpl_round(*x) + 0.5;
458-
*y = mpl_round(*y) + 0.5;
465+
*x = mpl_round(*x) + m_quantize_value;
466+
*y = mpl_round(*y) + m_quantize_value;
459467
}
460468
return code;
461469
}

0 commit comments

Comments
 (0)