@@ -1495,31 +1495,30 @@ RendererAgg::draw_regpoly_collection(const Py::Tuple& args) {
1495
1495
Py::Object
1496
1496
RendererAgg::draw_lines (const Py::Tuple& args) {
1497
1497
1498
-
1499
- _VERBOSE (" RendererAgg::draw_lines" );
1498
+ _VERBOSE (" RendererAgg::draw_lines" );
1500
1499
args.verify_length (4 );
1501
-
1500
+
1502
1501
Py::Object xo = args[1 ];
1503
1502
Py::Object yo = args[2 ];
1504
-
1503
+
1505
1504
PyArrayObject *xa = (PyArrayObject *) PyArray_ContiguousFromObject (xo.ptr (), PyArray_DOUBLE, 1 , 1 );
1506
-
1505
+
1507
1506
if (xa==NULL )
1508
1507
throw Py::TypeError (" RendererAgg::draw_lines expected numerix array" );
1509
-
1510
-
1508
+
1509
+
1511
1510
PyArrayObject *ya = (PyArrayObject *) PyArray_ContiguousFromObject (yo.ptr (), PyArray_DOUBLE, 1 , 1 );
1512
-
1511
+
1513
1512
if (ya==NULL )
1514
1513
throw Py::TypeError (" RendererAgg::draw_lines expected numerix array" );
1515
-
1516
-
1514
+
1515
+
1517
1516
size_t Nx = xa->dimensions [0 ];
1518
1517
size_t Ny = ya->dimensions [0 ];
1519
-
1518
+
1520
1519
if (Nx!=Ny)
1521
1520
throw Py::ValueError (Printf (" x and y must be equal length arrays; found %d and %d" , Nx, Ny).str ());
1522
-
1521
+
1523
1522
// call gc with snapto==True if line len is 2 to fix grid line
1524
1523
// problem
1525
1524
bool snapto = false ;
@@ -1531,116 +1530,274 @@ RendererAgg::draw_lines(const Py::Tuple& args) {
1531
1530
double y0 = *(double *)(ya->data + 0 *ya->strides [0 ]);
1532
1531
double y1 = *(double *)(ya->data + 1 *ya->strides [0 ]);
1533
1532
snapto = (x0==x1) || (y0==y1);
1534
-
1533
+
1535
1534
}
1536
-
1537
1535
GCAgg gc = GCAgg (args[0 ], dpi, snapto);
1538
-
1536
+
1539
1537
set_clipbox_rasterizer (gc.cliprect );
1540
- // path_t transpath(path, xytrans);
1541
- _process_alpha_mask (gc);
1542
-
1543
-
1538
+
1544
1539
Transformation* mpltransform = static_cast <Transformation*>(args[3 ].ptr ());
1545
-
1540
+
1546
1541
double a, b, c, d, tx, ty;
1547
1542
try {
1548
1543
mpltransform->affine_params_api (&a, &b, &c, &d, &tx, &ty);
1549
1544
}
1550
1545
catch (...) {
1551
1546
throw Py::ValueError (" Domain error on affine_params_api in RendererAgg::draw_lines" );
1552
1547
}
1553
-
1548
+
1554
1549
agg::trans_affine xytrans = agg::trans_affine (a,b,c,d,tx,ty);
1555
-
1556
-
1550
+
1551
+
1557
1552
agg::path_storage path;
1558
-
1559
-
1553
+
1554
+
1560
1555
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 );
1563
1559
bool moveto = true ;
1564
1560
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
+
1572
1584
thisx = *(double *)(xa->data + i*xa->strides [0 ]);
1573
1585
thisy = *(double *)(ya->data + i*ya->strides [0 ]);
1574
-
1575
-
1586
+
1576
1587
if (needNonlinear)
1577
1588
try {
1578
- mpltransform->nonlinear_only_api (&thisx, &thisy);
1589
+ mpltransform->nonlinear_only_api (&thisx, &thisy);
1579
1590
}
1580
1591
catch (...) {
1581
- moveto = true ;
1582
- continue ;
1592
+ moveto = true ;
1593
+ continue ;
1583
1594
}
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
+
1589
1600
// use agg's transformer?
1590
1601
xytrans.transform (&thisx, &thisy);
1591
1602
thisy = heightd - thisy; // flipy
1592
1603
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
+
1593
1621
// 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 ;
1595
1635
continue ;
1596
1636
}
1597
1637
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
+ }
1598
1669
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)
1604
1703
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 ;
1607
1726
}
1608
1727
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.
1609
1730
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
1623
1735
}
1624
1736
1737
+ path.line_to (maxX, maxY);
1625
1738
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++;
1627
1777
}
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
+
1629
1790
Py_XDECREF (xa);
1630
1791
Py_XDECREF (ya);
1631
-
1792
+
1632
1793
// typedef agg::conv_transform<agg::path_storage, agg::trans_affine> path_t;
1633
1794
// path_t transpath(path, xytrans);
1634
1795
_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
+
1641
1798
_VERBOSE (" RendererAgg::draw_lines DONE" );
1642
1799
return Py::Object ();
1643
-
1800
+
1644
1801
}
1645
1802
1646
1803
bool
0 commit comments