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

Skip to content

Generate path strings in C++ for PDF and PS #4197

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Mar 13, 2015
Merged
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
44 changes: 17 additions & 27 deletions lib/matplotlib/backends/backend_pdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
from matplotlib.backends.backend_mixed import MixedModeRenderer
from matplotlib.cbook import Bunch, is_string_like, \
get_realpath_and_stat, is_writable_file_like, maxdict
from matplotlib.mlab import quad2cubic
from matplotlib.figure import Figure
from matplotlib.font_manager import findfont, is_opentype_cff_font
from matplotlib.afm import AFM
Expand All @@ -48,6 +47,7 @@
from matplotlib.mathtext import MathTextParser
from matplotlib.transforms import Affine2D, BboxBase
from matplotlib.path import Path
from matplotlib import _path
from matplotlib import ttconv

# Overview
Expand Down Expand Up @@ -287,6 +287,17 @@ def __repr__(self):
def pdfRepr(self):
return self.op


class Verbatim(object):
"""Store verbatim PDF command content for later inclusion in the
stream."""
def __init__(self, x):
self._x = x

def pdfRepr(self):
return self._x


# PDF operators (not an exhaustive list)
_pdfops = dict(
close_fill_stroke=b'b', fill_stroke=b'B', fill=b'f', closepath=b'h',
Expand Down Expand Up @@ -1388,32 +1399,11 @@ def writePathCollectionTemplates(self):

@staticmethod
def pathOperations(path, transform, clip=None, simplify=None, sketch=None):
cmds = []
last_points = None
for points, code in path.iter_segments(transform, clip=clip,
simplify=simplify,
sketch=sketch):
if code == Path.MOVETO:
# This is allowed anywhere in the path
cmds.extend(points)
cmds.append(Op.moveto)
elif code == Path.CLOSEPOLY:
cmds.append(Op.closepath)
elif last_points is None:
# The other operations require a previous point
raise ValueError('Path lacks initial MOVETO')
elif code == Path.LINETO:
cmds.extend(points)
cmds.append(Op.lineto)
elif code == Path.CURVE3:
points = quad2cubic(*(list(last_points[-2:]) + list(points)))
cmds.extend(points[2:])
cmds.append(Op.curveto)
elif code == Path.CURVE4:
cmds.extend(points)
cmds.append(Op.curveto)
last_points = points
return cmds
return [Verbatim(_path.convert_to_string(
path, transform, clip, simplify, sketch,
6,
[Op.moveto.op, Op.lineto.op, b'', Op.curveto.op, Op.closepath.op],
True))]

def writePath(self, path, transform, clip=False, sketch=None):
if clip:
Expand Down
26 changes: 4 additions & 22 deletions lib/matplotlib/backends/backend_ps.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ def _fn_name(): return sys._getframe(1).f_code.co_name

from matplotlib.cbook import is_string_like, get_realpath_and_stat, \
is_writable_file_like, maxdict, file_requires_unicode
from matplotlib.mlab import quad2cubic
from matplotlib.figure import Figure

from matplotlib.font_manager import findfont, is_opentype_cff_font
Expand All @@ -36,6 +35,7 @@ def _fn_name(): return sys._getframe(1).f_code.co_name
from matplotlib._mathtext_data import uni2type1
from matplotlib.text import Text
from matplotlib.path import Path
from matplotlib import _path
from matplotlib.transforms import Affine2D

from matplotlib.backends.backend_mixed import MixedModeRenderer
Expand Down Expand Up @@ -531,27 +531,9 @@ def _convert_path(self, path, transform, clip=False, simplify=None):
self.height * 72.0)
else:
clip = None
for points, code in path.iter_segments(transform, clip=clip,
simplify=simplify):
if code == Path.MOVETO:
ps.append("%g %g m" % tuple(points))
elif code == Path.CLOSEPOLY:
ps.append("cl")
elif last_points is None:
# The other operations require a previous point
raise ValueError('Path lacks initial MOVETO')
elif code == Path.LINETO:
ps.append("%g %g l" % tuple(points))
elif code == Path.CURVE3:
points = quad2cubic(*(list(last_points[-2:]) + list(points)))
ps.append("%g %g %g %g %g %g c" %
tuple(points[2:]))
elif code == Path.CURVE4:
ps.append("%g %g %g %g %g %g c" % tuple(points))
last_points = points

ps = "\n".join(ps)
return ps
return _path.convert_to_string(
path, transform, clip, simplify, None,
6, [b'm', b'l', b'', b'c', b'cl'], True).decode('ascii')

def _get_clip_path(self, clippath, clippath_transform):
key = (clippath, id(clippath_transform))
Expand Down
4 changes: 3 additions & 1 deletion lib/matplotlib/backends/backend_svg.py
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,9 @@ def _convert_path(self, path, transform=None, clip=None, simplify=None):
clip = (0.0, 0.0, self.width, self.height)
else:
clip = None
return _path.convert_to_svg(path, transform, clip, simplify, 6)
return _path.convert_to_string(
path, transform, clip, simplify, None, 6,
[b'M', b'L', b'Q', b'C', b'z'], False).decode('ascii')

def draw_path(self, gc, path, transform, rgbFace=None):
trans_and_flip = self._make_flip_transform(transform)
Expand Down
2 changes: 2 additions & 0 deletions lib/matplotlib/mlab.py
Original file line number Diff line number Diff line change
Expand Up @@ -4007,6 +4007,8 @@ def quad2cubic(q0x, q0y, q1x, q1y, q2x, q2y):
points of a quadratic curve, and the output is a tuple of *x* and
*y* coordinates of the four control points of the cubic curve.
"""
# TODO: Candidate for deprecation -- no longer used internally

# c0x, c0y = q0x, q0y
c1x, c1y = q0x + 2./3. * (q1x - q0x), q0y + 2./3. * (q1y - q0y)
c2x, c2y = c1x + 1./3. * (q2x - q0x), c1y + 1./3. * (q2y - q0y)
Expand Down
191 changes: 139 additions & 52 deletions src/_path.h
Original file line number Diff line number Diff line change
Expand Up @@ -939,79 +939,166 @@ void cleanup_path(PathIterator &path,
}
}

void quad2cubic(double x0, double y0,
double x1, double y1,
Copy link
Member

Choose a reason for hiding this comment

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

Do we consider this part of the public API?

Copy link
Member Author

Choose a reason for hiding this comment

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

No. It's in a module with a leading underscore (_path).

double x2, double y2,
double *outx, double *outy)
{

outx[0] = x0 + 2./3. * (x1 - x0);
outy[0] = y0 + 2./3. * (y1 - y0);
outx[1] = outx[0] + 1./3. * (x2 - x0);
outy[1] = outy[0] + 1./3. * (y2 - y0);
outx[2] = x2;
outy[2] = y2;
}

char *__append_to_string(char *p, char *buffer, size_t buffersize,
const char *content)
{
int buffersize_int = (int)buffersize;

for (const char *i = content; *i; ++i) {
if (p < buffer || p - buffer >= buffersize_int) {
return NULL;
}

*p++ = *i;
}

return p;
}

template <class PathIterator>
void convert_to_svg(PathIterator &path,
agg::trans_affine &trans,
agg::rect_d &clip_rect,
bool simplify,
int precision,
char *buffer,
size_t *buffersize)
int __convert_to_string(PathIterator &path,
int precision,
char **codes,
bool postfix,
char *buffer,
size_t *buffersize)
{
#if PY_VERSION_HEX < 0x02070000
char format[64];
snprintf(format, 64, "%s.%dg", "%", precision);
#endif

typedef agg::conv_transform<py::PathIterator> transformed_path_t;
typedef PathNanRemover<transformed_path_t> nan_removal_t;
typedef PathClipper<nan_removal_t> clipped_t;
typedef PathSimplifier<clipped_t> simplify_t;

bool do_clip = (clip_rect.x1 < clip_rect.x2 && clip_rect.y1 < clip_rect.y2);

transformed_path_t tpath(path, trans);
nan_removal_t nan_removed(tpath, true, path.has_curves());
clipped_t clipped(nan_removed, do_clip, clip_rect);
simplify_t simplified(clipped, simplify, path.simplify_threshold());

char *p = buffer;
double x[3];
double y[3];
double last_x = 0.0;
double last_y = 0.0;

const char codes[] = { 'M', 'L', 'Q', 'C' };
const int waits[] = { 1, 1, 2, 3 };

int wait = 0;
const int sizes[] = { 1, 1, 2, 3 };
int size = 0;
unsigned code;
double x = 0, y = 0;
while ((code = simplified.vertex(&x, &y)) != agg::path_cmd_stop) {
if (wait == 0) {
*p++ = '\n';

if (code == 0x4f) {
*p++ = 'z';
*p++ = '\n';
continue;
while ((code = path.vertex(&x[0], &y[0])) != agg::path_cmd_stop) {
if (code == 0x4f) {
if ((p = __append_to_string(p, buffer, *buffersize, codes[4])) == NULL) return 1;
} else if (code < 5) {
size = sizes[code - 1];
Copy link
Member

Choose a reason for hiding this comment

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

This should probably have a check that code < 5 so we can't walk out the end.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ok. This should be fixed now as well.


for (int i = 1; i < size; ++i) {
unsigned subcode = path.vertex(&x[i], &y[i]);
if (subcode != code) {
return 2;
}
}

*p++ = codes[code - 1];
wait = waits[code - 1];
} else {
*p++ = ' ';
}
/* For formats that don't support quad curves, convert to
cubic curves */
if (code == CURVE3 && codes[code - 1][0] == '\0') {
quad2cubic(last_x, last_y, x[0], y[0], x[1], y[1], x, y);
code++;
size = 3;
}

if (!postfix) {
if ((p = __append_to_string(p, buffer, *buffersize, codes[code - 1])) == NULL) return 1;
if ((p = __append_to_string(p, buffer, *buffersize, " ")) == NULL) return 1;
}

for (int i = 0; i < size; ++i) {
#if PY_VERSION_HEX >= 0x02070000
char *str;
str = PyOS_double_to_string(x, 'g', precision, 0, NULL);
p += snprintf(p, *buffersize - (p - buffer), "%s", str);
PyMem_Free(str);
*p++ = ' ';
str = PyOS_double_to_string(y, 'g', precision, 0, NULL);
p += snprintf(p, *buffersize - (p - buffer), "%s", str);
PyMem_Free(str);
char *str;
str = PyOS_double_to_string(x[i], 'g', precision, 0, NULL);
if ((p = __append_to_string(p, buffer, *buffersize, str)) == NULL) {
PyMem_Free(str);
return 1;
}
PyMem_Free(str);
if ((p = __append_to_string(p, buffer, *buffersize, " ")) == NULL) return 1;
str = PyOS_double_to_string(y[i], 'g', precision, 0, NULL);
if ((p = __append_to_string(p, buffer, *buffersize, str)) == NULL) {
PyMem_Free(str);
return 1;
}
PyMem_Free(str);
if ((p = __append_to_string(p, buffer, *buffersize, " ")) == NULL) return 1;
#else
char str[64];
PyOS_ascii_formatd(str, 64, format, x);
p += snprintf(p, *buffersize - (p - buffer), "%s", str);
*p++ = ' ';
PyOS_ascii_formatd(str, 64, format, y);
p += snprintf(p, *buffersize - (p - buffer), "%s", str);
char str[64];
PyOS_ascii_formatd(str, 64, format, x[i]);
if ((p = __append_to_string(p, buffer, *buffersize, str)) == NULL) return 1;
p = __append_to_string(p, buffer, *buffersize, " ");
PyOS_ascii_formatd(str, 64, format, y[i]);
if ((p = __append_to_string(p, buffer, *buffersize, str)) == NULL) return 1;
if ((p = __append_to_string(p, buffer, *buffersize, " ")) == NULL) return 1;
#endif
}

if (postfix) {
if ((p = __append_to_string(p, buffer, *buffersize, codes[code - 1])) == NULL) return 1;
}

last_x = x[size - 1];
last_y = y[size - 1];
} else {
// Unknown code value
return 2;
}

--wait;
if ((p = __append_to_string(p, buffer, *buffersize, "\n")) == NULL) return 1;
}

*p = '\0';
*buffersize = p - buffer;

return 0;
}

template <class PathIterator>
int convert_to_string(PathIterator &path,
agg::trans_affine &trans,
agg::rect_d &clip_rect,
bool simplify,
SketchParams sketch_params,
int precision,
char **codes,
bool postfix,
char *buffer,
size_t *buffersize)
{
typedef agg::conv_transform<py::PathIterator> transformed_path_t;
typedef PathNanRemover<transformed_path_t> nan_removal_t;
typedef PathClipper<nan_removal_t> clipped_t;
typedef PathSimplifier<clipped_t> simplify_t;
typedef agg::conv_curve<simplify_t> curve_t;
typedef Sketch<curve_t> sketch_t;

bool do_clip = (clip_rect.x1 < clip_rect.x2 && clip_rect.y1 < clip_rect.y2);

transformed_path_t tpath(path, trans);
nan_removal_t nan_removed(tpath, true, path.has_curves());
clipped_t clipped(nan_removed, do_clip, clip_rect);
simplify_t simplified(clipped, simplify, path.simplify_threshold());

if (sketch_params.scale == 0.0) {
return __convert_to_string(simplified, precision, codes, postfix, buffer, buffersize);
} else {
curve_t curve(simplified);
sketch_t sketch(curve, sketch_params.scale, sketch_params.length, sketch_params.randomness);
return __convert_to_string(sketch, precision, codes, postfix, buffer, buffersize);
}

}

#endif
Loading