8000 added allans draw lines optimization · matplotlib/matplotlib@b79039a · GitHub
[go: up one dir, main page]

Skip to content

Commit b79039a

Browse files
committed
added allans draw lines optimization
svn path=/trunk/matplotlib/; revision=3486
1 parent f985fc2 commit b79039a

File tree

3 files changed

+236
-76
lines changed

3 files changed

+236
-76
lines changed

CHANGELOG

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
2007-07-09 Applied Allan's draw_lines agg optimization. JDH
2+
3+
14
2007-07-08 Applied Carl Worth's patch to fix cairo draw_arc - SC
25

36
2007-07-07 fixed bug 1712099: xpdf distiller on windows - DSD

lib/matplotlib/axes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2946,7 +2946,7 @@ def get_handles():
29462946
labels = []
29472947
for handle in get_handles():
29482948
label = handle.get_label()
2949-
if label is not None and label != '' and label[0] != '_':
2949+
if label is not None and label != '' and not label.startswith('_'):
29502950
handles.append(handle)
29512951
labels.append(label)
29522952
loc = popd(kwargs, 'loc', 1)

src/_backend_agg.cpp

Lines changed: 232 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1495,31 +1495,30 @@ RendererAgg::draw_regpoly_collection(const Py::Tuple& args) {
14951495
Py::Object
14961496
RendererAgg::draw_lines(const Py::Tuple& args) {
14971497

1498-
1499-
_VERBOSE("RendererAgg::draw_lines");
1498+
_VERBOSE("RendererAgg::draw_lines");
15001499
args.verify_length(4);
1501-
1500+
15021501
Py::Object xo = args[1];
15031502
Py::Object yo = args[2];
1504-
1503+
15051504
PyArrayObject *xa = (PyArrayObject *) PyArray_ContiguousFromObject(xo.ptr(), PyArray_DOUBLE, 1, 1);
1506-
1505+
15071506
if (xa==NULL)
15081507
throw Py::TypeError("RendererAgg::draw_lines expected numerix array");
1509-
1510-
1508+
1509+
15111510
PyArrayObject *ya = (PyArrayObject *) PyArray_ContiguousFromObject(yo.ptr(), PyArray_DOUBLE, 1, 1);
1512-
1511+
15131512
if (ya==NULL)
15141513
throw Py::TypeError("RendererAgg::draw_lines expected numerix array");
1515-
1516-
1514+
1515+
15171516
size_t Nx = xa->dimensions[0];
15181517
size_t Ny = ya->dimensions[0];
1519-
1518+
15201519
if (Nx!=Ny)
15211520
throw Py::ValueError(Printf("x and y must be equal length arrays; found %d and %d", Nx, Ny).str());
1522-
1521+
15231522
// call gc with snapto==True if line len is 2 to fix grid line
15241523
// problem
15251524
bool snapto = false;
@@ -1531,116 +1530,274 @@ RendererAgg::draw_lines(const Py::Tuple& args) {
15311530
double y0 = *(double *)(ya->data + 0*ya->strides[0]);
15321531
double y1 = *(double *)(ya->data + 1*ya->strides[0]);
15331532
snapto = (x0==x1) || (y0==y1);
1534-
1533+
15351534
}
1536-
15371535
GCAgg gc = GCAgg(args[0], dpi, snapto);
1538-
1536+
15391537
set_clipbox_rasterizer(gc.cliprect);
1540-
//path_t transpath(path, xytrans);
1541-
_process_alpha_mask(gc);
1542-
1543-
1538+
15441539
Transformation* mpltransform = static_cast<Transformation*>(args[3].ptr());
1545-
1540+
15461541
double a, b, c, d, tx, ty;
15471542
try {
15481543
mpltransform->affine_params_api(&a, &b, &c, &d, &tx, &ty);
15491544
}
15501545
catch(...) {
15511546
throw Py::ValueError("Domain error on affine_params_api in RendererAgg::draw_lines");
15521547
}
1553-
1548+
15541549
agg::trans_affine xytrans = agg::trans_affine(a,b,c,d,tx,ty);
1555-
1556-
1550+
1551+
15571552
agg::path_storage path;
1558-
1559-
1553+
1554+
15601555
bool needNonlinear = mpltransform->need_nonlinear_api();
1561-
1562-
double thisx, thisy;
1556+
1557+
double thisx(0.0), thisy(0.0);
1558+
double origdx(0.0), origdy(0.0), origdNorm2(0);
15631559
bool moveto = true;
15641560
double heightd = height;
1565-
1566-
double lastx(-2.0), lasty(-2.0);
1567-
1568-
size_t i(0);
1569-
bool more(true);
1570-
for (i=0; i<Nx; i++) {
1571-
more = true;
1561+
1562+
double lastx(0), lasty(0);
1563+
double lastWrittenx(0), lastWritteny(0);
1564+
bool clipped = false;
1565+
1566+
bool haveMin = false, lastMax = true;
1567+
double dnorm2Min(0), dnorm2Max(0);
1568+
double maxX(0), maxY(0), minX(0), minY(0);
1569+
1570+
double totdx, totdy, totdot;
1571+
double paradx, parady, paradNorm2;
1572+
double perpdx, perpdy, perpdNorm2;
1573+
1574+
int counter = 0;
1575+
//idea: we can skip drawing many lines: lines < 1 pixel in length, lines
1576+
//outside of the drawing area, and we can combine sequential parallel lines
1577+
//into a single line instead of redrawing lines over the same points.
1578+
//The loop below works a bit like a state machine, where what it does depends
1579+
//on what it did in the last looping. To test whether sequential lines
1580+
//are close to parallel, I calculate the distance moved perpendicular to the
1581+
//last line. Once it gets too big, the lines cannot be combined.
1582+
for (size_t i=0; i<Nx; i++) {
1583+
15721584
thisx = *(double *)(xa->data + i*xa->strides[0]);
15731585
thisy = *(double *)(ya->data + i*ya->strides[0]);
1574-
1575-
1586+
15761587
if (needNonlinear)
15771588
try {
1578-
mpltransform->nonlinear_only_api(&thisx, &thisy);
1589+
mpltransform->nonlinear_only_api(&thisx, &thisy);
15791590
}
15801591
catch (...) {
1581-
moveto = true;
1582-
continue;
1592+
moveto = true;
1593+
continue;
15831594
}
1584-
if (MPL_isnan64(thisx) || MPL_isnan64(thisy)) {
1585-
moveto = true;
1586-
continue;
1587-
}
1588-
1595+
if (MPL_isnan64(thisx) || MPL_isnan64(thisy)) {
1596+
moveto = true;
1597+
continue;
1598+
}
1599+
15891600
//use agg's transformer?
15901601
xytrans.transform(&thisx, &thisy);
15911602
thisy = heightd - thisy; //flipy
15921603

1604+
if (snapto) {
1605+
//disable subpixel rendering for horizontal or vertical lines of len=2
1606+
//because it causes irregular line widths for grids and ticks
1607+
thisx = (int)thisx + 0.5;
1608+
thisy = (int)thisy + 0.5;
1609+
}
1610+
1611+
//if we are starting a new path segment, move to the first point + init
1612+
if(moveto){
1613+
path.move_to(thisx, thisy);
1614+
lastx = thisx;
1615+
lasty = thisy;
1616+
origdNorm2 = 0; //resets the orig-vector variables (see if-statement below)
1617+
moveto = false;
1618+
continue;
1619+
}
1620+
15931621
//don't render line segments less that on pixel long!
1594-
if (!moveto && (i>0) && fabs(thisx-lastx)<1.0 && fabs(thisy-lasty)<1.0) {
1622+
if (fabs(thisx-lastx) < 1.0 && fabs(thisy-lasty) < 1.0 ){
1623+
continue; //don't update lastx this time!
1624+
}
1625+
1626+
//skip any lines that are outside the drawing area. Note: More lines
1627+
//could be clipped, but a more involved calculation would be needed
1628+
if( (thisx < 0 && lastx < 0 ) ||
1629+
(thisx > width && lastx > width ) ||
1630+
(thisy < 0 && lasty < 0 ) ||
1631+
(thisy > height && lasty > height) ){
1632+
lastx = thisx;
1633+
lasty = thisy;
1634+
clipped = true;
15951635
continue;
15961636
}
15971637

1638+
//if we have no orig vector, set it to this vector and continue.
1639+
//this orig vector is the reference vector we will build up the line to
1640+
if(origdNorm2 == 0){
1641+
//if we clipped after the moveto but before we got here, redo the moveto
1642+
if(clipped){
1643+
path.move_to(lastx, lasty);
1644+
clipped = false;
1645+
}
1646+
1647+
origdx = thisx - lastx;
1648+
origdy = thisy - lasty;
1649+
origdNorm2 = origdx*origdx + origdy*origdy;
1650+
1651+
//set all the variables to reflect this new orig vecor
1652+
dnorm2Max = origdNorm2;
1653+
dnorm2Min = 0;
1654+
haveMin = false;
1655+
lastMax = true;
1656+
maxX = thisx;
1657+
maxY = thisy;
1658+
minX = lastx;
1659+
minY = lasty;
1660+
1661+
lastWrittenx = lastx;
1662+
lastWritteny = lasty;
1663+
1664+
//set the last point seen
1665+
lastx = thisx;
1666+
lasty = thisy;
1667+
continue;
1668+
}
15981669

1599-
lastx = thisx;
1600-
lasty = thisy;
1601-
if (snapto) {
1602-
//disable subpixel rendering for horizontal or vertical lines
1603-
//because it causes irregular line widths for grids and ticks
1670+
//if got to here, then we have an orig vector and we just got
1671+
//a vector in the sequence.
1672+
1673+
//check that the perpendicular distance we have moved from the
1674+
//last written point compared to the line we are building is not too
1675+
//much. If o is the orig vector (we are building on), and v is the vector
1676+
//from the last written point to the current point, then the perpendicular
1677+
//vector is p = v - (o.v)o, and we normalize o (by dividing the
1678+
//second term by o.o).
1679+
1680+
//get the v vector
1681+
totdx = thisx - lastWrittenx;
1682+
totdy = thisy - lastWritteny;
1683+
totdot = origdx*totdx + origdy*totdy;
1684+
1685+
//get the para vector ( = (o.v)o/(o.o) )
1686+
paradx = totdot*origdx/origdNorm2;
1687+
parady = totdot*origdy/origdNorm2;
1688+
paradNorm2 = paradx*paradx + parady*parady;
1689+
1690+
//get the perp vector ( = v - para )
1691+
perpdx = totdx - paradx;
1692+
perpdy = totdy - parady;
1693+
perpdNorm2 = perpdx*perpdx + perpdy*perpdy;
1694+
1695+
//if the perp vector is less than some number of (squared) pixels in size,
1696+
//then merge the current vector
1697+
if(perpdNorm2 < 0.25 ){
1698+
//check if the current vector is parallel or
1699+
//anti-parallel to the orig vector. If it is parallel, test
1700+
//if it is the longest of the vectors we are merging in that direction.
1701+
//If anti-p, test if it is the longest in the opposite direction (the
1702+
//min of our final line)
16041703

1605-
thisx = (int)thisx + 0.5;
1606-
thisy = (int)thisy + 0.5;
1704+
lastMax = false;
1705+
if(totdot >= 0){
1706+
if(paradNorm2 > dnorm2Max){
1707+
lastMax = true;
1708+
dnorm2Max = paradNorm2;
1709+
maxX = lastWrittenx + paradx;
1710+
maxY = lastWritteny + parady;
1711+
}
1712+
}
1713+
else{
1714+
1715+
haveMin = true;
1716+
if(paradNorm2 > dnorm2Min){
1717+
dnorm2Min = paradNorm2;
1718+
minX = lastWrittenx + paradx;
1719+
minY = lastWritteny + parady;
1720+
}
1721+
}
1722+
1723+
lastx = thisx;
1724+
lasty = thisy;
1725+
continue;
16071726
}
16081727

1728+
//if we get here, then this vector was not similar enough to the line
1729+
//we are building, so we need to draw that line and start the next one.
16091730

1610-
if (moveto)
1611-
path.move_to(thisx, thisy);
1612-
else
1613-
path.line_to(thisx, thisy);
1614-
1615-
moveto = false;
1616-
if ((i>0) & (i%10000)==0) {
1617-
//draw the path in chunks
1618-
//std::cout << "rendering chunk " << i << std::endl;
1619-
_render_lines_path(path, gc);
1620-
path.remove_all();
1621-
path.move_to(thisx, thisy);
1622-
more = false;
1731+
//if the line needs to extend in the opposite direction from the direction
1732+
//we are drawing in, move back to we start drawing from back there.
1733+
if(haveMin){
1734+
path.line_to(minX, minY); //would be move_to if not for artifacts
16231735
}
16241736

1737+
path.line_to(maxX, maxY);
16251738

1626-
//std::cout << "draw lines " << thisx << " " << thisy << std::endl;
1739+
//if we clipped some segments between this line and the next line
1740+
//we are starting, we also need to move to the last point.
1741+
if(clipped){
1742+
path.move_to(lastx, lasty);
1743+
}
1744+
else if(!lastMax){
1745+
//if the last line was not the longest line, then move back to the end
1746+
//point of the last line in the sequence. Only do this if not clipped,
1747+
//since in that case lastx,lasty is not part of the line just drawn.
1748+
path.line_to(lastx, lasty); //would be move_to if not for artifacts
1749+
}
1750+
1751+
//std::cout << "draw lines (" << lastx << ", " << lasty << ")" << std::endl;
1752+
1753+
//now reset all the variables to get ready for the next line
1754+
1755+
origdx = thisx - lastx;
1756+
origdy = thisy - lasty;
1757+
origdNorm2 = origdx*origdx + origdy*origdy;
1758+
1759+
dnorm2Max = origdNorm2;
1760+
dnorm2Min = 0;
1761+
haveMin = false;
1762+
lastMax = true;
1763+
maxX = thisx;
1764+
maxY = thisy;
1765+
minX = lastx;
1766+
minY = lasty;
1767+
1768+
lastWrittenx = lastx;
1769+
lastWritteny = lasty;
1770+
1771+
clipped = false;
1772+
1773+
lastx = thisx;
1774+
lasty = thisy;
1775+
1776+
counter++;
16271777
}
1628-
1778+
1779+
//draw the last line, which is usually not drawn in the loop
1780+
if(origdNorm2 != 0){
1781+
if(haveMin){
1782+
path.line_to(minX, minY); //would be move_to if not for artifacts
1783+
}
1784+
1785+
path.line_to(maxX, maxY);
1786+
}
1787+
1788+
//std::cout << "drew " << counter+1 << " lines" << std::endl;
1789+
16291790
Py_XDECREF(xa);
16301791
Py_XDECREF(ya);
1631-
1792+
16321793
//typedef agg::conv_transform<agg::path_storage, agg::trans_affine> path_t;
16331794
//path_t transpath(path, xytrans);
16341795
_VERBOSE("RendererAgg::draw_lines rendering lines path");
1635-
if (more){
1636-
//render the rest
1637-
///std::cout << "rendering the rest" << std::endl;
1638-
_render_lines_path(path, gc);
1639-
}
1640-
1796+
_render_lines_path(path, gc);
1797+
16411798
_VERBOSE("RendererAgg::draw_lines DONE");
16421799
return Py::Object();
1643-
1800+
16441801
}
16451802

16461803
bool

0 commit comments

Comments
 (0)
0