diff --git a/src/db/db/dbFillTool.cc b/src/db/db/dbFillTool.cc index 73f7e434d..ce0053598 100644 --- a/src/db/db/dbFillTool.cc +++ b/src/db/db/dbFillTool.cc @@ -27,6 +27,7 @@ #include "dbRegion.h" #include "dbCell.h" #include "tlIntervalMap.h" +#include "tlMath.h" namespace db { @@ -268,9 +269,107 @@ optimize_offset (const db::Polygon &fp, const db::AreaMap &am) return db::Vector (xshift, yshift); } +class GenericRasterizer +{ +public: + GenericRasterizer () + : m_row_step (), m_column_step (), m_row_steps (0), m_column_steps (0), m_origin () + { + // .. nothing yet .. + } + + GenericRasterizer (const db::Polygon &fp, const db::Vector &row_step, const db::Vector &column_step, const db::Point &origin) + : m_row_step (row_step), m_column_step (column_step), m_row_steps (0), m_column_steps (0), m_origin (origin) + { + db::Coord dx = row_step.x (); + db::Coord dy = column_step.y (); + + if (row_step.y () == 0) { + m_row_steps = 1; + } else { + m_row_steps = tl::lcm (dy, std::abs (row_step.y ())) / std::abs (row_step.y ()); + } + + if (column_step.x () == 0) { + m_column_steps = 1; + } else { + m_column_steps = tl::lcm (dx, std::abs (column_step.x ())) / std::abs (column_step.x ()); + } + + db::Box fp_bbox = fp.box (); + + db::Coord ddx = dx * db::Coord (m_row_steps) - column_step.x () * ((db::Coord (m_row_steps) * row_step.y ()) / dy); + db::Coord ddy = dy * db::Coord (m_column_steps) - row_step.y () * ((db::Coord (m_column_steps) * column_step.x ()) / dx); + + // round polygon bbox + db::Coord fp_left = db::Coord (tl::round_down (fp_bbox.left () - origin.x (), ddx)) + origin.x (); + db::Coord fp_bottom = db::Coord (tl::round_down (fp_bbox.bottom () - origin.y (), ddy)) + origin.y (); + db::Coord fp_right = db::Coord (tl::round_up (fp_bbox.right () - origin.x (), ddx)) + origin.x (); + db::Coord fp_top = db::Coord (tl::round_up (fp_bbox.top () - origin.y (), ddy)) + origin.y (); + fp_bbox = db::Box (fp_left, fp_bottom, fp_right, fp_top); + + size_t nx = fp_bbox.width () / ddx; + size_t ny = fp_bbox.height () / ddy; + + tl_assert (fp.box ().inside (fp_bbox)); + + if (nx == 0 || ny == 0) { + // nothing to rasterize: + return; + } + + m_area_maps.resize (m_row_steps * m_column_steps); + + for (unsigned int ic = 0; ic < m_column_steps; ++ic) { + + for (unsigned int ir = 0; ir < m_row_steps; ++ir) { + + db::Vector dr = m_row_step * long (ir); + db::Vector dc = m_column_step * long (ic); + + long ir_add = dc.x () / dx; + dr -= m_row_step * ir_add; + + long ic_add = dr.y () / dy; + dc -= m_column_step * ic_add; + + db::AreaMap &am = m_area_maps [ic * m_row_steps + ir]; + am.reinitialize (db::Point (fp_left, fp_bottom) + dr + dc, db::Vector (ddx, ddy), db::Vector (dx, dy), nx, ny); + + db::rasterize (fp, am); + + } + + } + } + + const db::Point &p0 () const { return m_origin; } + + unsigned int row_steps () const { return m_row_steps; } + unsigned int column_steps () const { return m_column_steps; } + + const db::AreaMap &area_map (unsigned int ir, unsigned int ic) const + { + return m_area_maps [ic * m_row_steps + ir]; + } + + db::Vector area_map_offset (unsigned int ir, unsigned int ic) const + { + return m_row_step * long (ir) + m_column_step * long (ic); + } + +private: + std::vector m_area_maps; + db::Vector m_row_step, m_column_step; + unsigned int m_row_steps, m_column_steps; + db::Point m_origin; +}; + + static bool rasterize_simple (const db::Polygon &fp, const db::Box &fc_bbox, const db::Point &p0, db::AreaMap &am) { + db::Coord dx = fc_bbox.width (); db::Coord dy = fc_bbox.height (); @@ -439,19 +538,6 @@ printf("@@@ -> n=%d\n", int(n)); fflush(stdout); // @@@ return true; } -static db::IMatrix2d -compute_shear_matrix (const db::Vector &r, const db::Vector &c) -{ - double det = r.x () * c.y () - r.y () * c.x (); - - double m11 = c.y () * r.x () / det; - double m22 = m11; - double m12 = -c.x () * r.x () / det; - double m21 = -c.y () * r.y () / det; - - return IMatrix2d (m11, m12, m21, m22); -} - DB_PUBLIC bool fill_region (db::Cell *cell, const db::Polygon &fp0, db::cell_index_type fill_cell_index, const db::Box &fc_bbox, const db::Point &origin, bool enhanced_fill, std::vector *remaining_parts, const db::Vector &fill_margin) @@ -463,73 +549,6 @@ fill_region (db::Cell *cell, const db::Polygon &fp0, db::cell_index_type fill_ce return fill_region (cell, fp0, fill_cell_index, fc_bbox.p1 () - db::Point (), db::Vector (fc_bbox.width (), 0), db::Vector (0, fc_bbox.height ()), origin, enhanced_fill, remaining_parts, fill_margin); } -static db::Polygon -produce_fill_stripe (const db::Vector &row_step, const db::Vector &column_step, long n) -{ - if (column_step.x () == 0) { - - db::Coord ymin = std::min (0, row_step.y ()); - db::Coord ymax = std::max (0, row_step.y ()) + n * column_step.y (); - - return db::Polygon (db::Box (0, ymin, row_step.x (), ymax)); - - } else { - - db::Coord xmin = 0; - db::Coord xmax = row_step.x (); - - db::Coord ymin = 0; - db::Coord ymax = n * column_step.y (); - - std::vector pts; - pts.reserve (n * 2); - - for (long i = 0; i < n; ++i) { - - db::Coord x = xmin + i * column_step.x (); - db::Coord y = i * column_step.y (); - - if (i == 0) { - pts.push_back (db::Point (x, ymin)); - } else { - pts.push_back (db::Point (x, y)); - } - if (i == n - 1) { - pts.push_back (db::Point (x, ymax)); - } else { - pts.push_back (db::Point (x, y + column_step.y ())); - } - - } - - for (long i = n; i > 0; ) { - - --i; - - db::Coord x = xmax + i * column_step.x (); - db::Coord y = i * column_step.y (); - - if (i == n - 1) { - pts.push_back (db::Point (x, ymax)); - } else { - pts.push_back (db::Point (x, y + column_step.y ())); - } - if (i == 0) { - pts.push_back (db::Point (x, ymin)); - } else { - pts.push_back (db::Point (x, y)); - } - - } - - db::Polygon p; - p.assign_hull (pts.begin (), pts.end ()); - return p; - - } -} - - DB_PUBLIC bool fill_region (db::Cell *cell, const db::Polygon &fp0, db::cell_index_type fill_cell_index, const db::Vector &kernel_origin, const db::Vector &row_step, const db::Vector &column_step, const db::Point &origin, bool enhanced_fill, std::vector *remaining_parts, const db::Vector &fill_margin) @@ -542,14 +561,6 @@ fill_region (db::Cell *cell, const db::Polygon &fp0, db::cell_index_type fill_ce throw tl::Exception (tl::to_string (tr ("Invalid row or column step vectors in fill_region: row_step x column_step vector vector product must be > 0"))); } - db::IMatrix2d shear = compute_shear_matrix (row_step, column_step); - db::IMatrix2d inverse_shear = shear.inverted (); - - tl_assert (shear.trans (row_step).y () == 0); - tl_assert (shear.trans (row_step).x () == row_step.x ()); - tl_assert (shear.trans (column_step).x () == 0); - tl_assert (shear.trans (column_step).y () == column_step.y ()); - std::vector filled_regions; db::EdgeProcessor ep; @@ -581,62 +592,68 @@ fill_region (db::Cell *cell, const db::Polygon &fp0, db::cell_index_type fill_ce filled_regions.clear (); bool any_fill = false; - for (std::vector ::const_iterator fpr = fpb.begin (); fpr != fpb.end (); ++fpr) { - - db::Polygon fp = fpr->transformed (shear); + for (std::vector ::const_iterator fp = fpb.begin (); fp != fpb.end (); ++fp) { size_t ninsts = 0; - db::AreaMap am; + GenericRasterizer am (*fp, row_step, column_step, origin); - db::Box raster_box (0, 0, row_step.x (), column_step.y ()); + // @@@ optimize fill offset ... - // Rasterize to determine fill regions - // NOTE: rasterization happens post-shear transformation (e.g. with a rectangular kernel) - if ((enhanced_fill && rasterize_extended (fp, raster_box, am)) || (!enhanced_fill && rasterize_simple (fp, raster_box, origin, am))) { + for (unsigned int ir = 0; ir < am.row_steps (); ++ir) { - size_t nx = am.nx (); - size_t ny = am.ny (); + for (unsigned int ic = 0; ic < am.column_steps (); ++ic) { - db::AreaMap::area_type amax = am.pixel_area (); + const db::AreaMap &am1 = am.area_map (ir, ic); - // Create the fill cell instances - for (size_t i = 0; i < nx; ++i) { + size_t nx = am1.nx (); + size_t ny = am1.ny (); - for (size_t j = 0; j < ny; ) { + // Create the fill cell instances + for (size_t i = 0; i < nx; ++i) { - size_t jj = j + 1; - if (am.get (i, j) >= amax) { + for (size_t j = 0; j < ny; ) { + + size_t jj = j + 1; + if (am1.get (i, j) == am1.pixel_area ()) { + + while (jj != ny && am1.get (i, jj) == am1.pixel_area ()) { + ++jj; + } + + ninsts += (jj - j); + + db::Vector p0 = (am1.p0 () - db::Point ()) - kernel_origin; + p0 += db::Vector (i * am1.d ().x (), j * am1.d ().y ()); + + db::CellInstArray array; + + if (jj > j + 1) { + array = db::CellInstArray (db::CellInst (fill_cell_index), db::Trans (p0), db::Vector (0, am1.d ().y ()), db::Vector (), (unsigned long) (jj - j), 1); + } else { + array = db::CellInstArray (db::CellInst (fill_cell_index), db::Trans (p0)); + } + + cell->insert (array); + + if (remaining_parts) { + if (am1.d ().y () == am1.p ().y ()) { + filled_regions.push_back (db::Polygon (db::Box (db::Point (), db::Point (am1.p ().x (), am1.p ().y () * (jj - j))).moved (kernel_origin + p0))); + } else { + for (size_t k = 0; k < jj - j; ++k) { + filled_regions.push_back (db::Polygon (db::Box (db::Point (), db::Point () + am1.p ()).moved (kernel_origin + p0 + db::Vector (0, am1.d ().y () * db::Coord (k))))); + } + } + } + + any_fill = true; - while (jj != ny && am.get (i, jj) >= amax) { - ++jj; } - ninsts += (jj - j); - - db::Vector p0 (inverse_shear.trans (am.p0 ()) - kernel_origin); - p0 += row_step * long (i) + column_step * long (j); - - db::CellInstArray array; - - if (jj > j + 1) { - array = db::CellInstArray (db::CellInst (fill_cell_index), db::Trans (p0), column_step, db::Vector (), (unsigned long) (jj - j), 1); - } else { - array = db::CellInstArray (db::CellInst (fill_cell_index), db::Trans (p0)); - } - - cell->insert (array); - - if (remaining_parts) { - filled_regions.push_back (produce_fill_stripe (row_step, column_step, long (jj - j)).moved (p0 + kernel_origin)); - } - - any_fill = true; + j = jj; } - j = jj; - } } @@ -644,7 +661,7 @@ fill_region (db::Cell *cell, const db::Polygon &fp0, db::cell_index_type fill_ce } if (tl::verbosity () >= 30 && ninsts > 0) { - tl::info << "Part " << fpr->to_string (); + tl::info << "Part " << fp->to_string (); tl::info << "Created " << ninsts << " instances"; } @@ -773,6 +790,8 @@ fill_region_repeat (db::Cell *cell, const db::Region &fr, db::cell_index_type fi new_fill_region.swap (remaining); fill_region = &new_fill_region; + break; // @@@ + } } diff --git a/src/db/db/dbPolygonTools.cc b/src/db/db/dbPolygonTools.cc index 074494acf..45905b4e0 100644 --- a/src/db/db/dbPolygonTools.cc +++ b/src/db/db/dbPolygonTools.cc @@ -1563,8 +1563,35 @@ AreaMap::AreaMap () mp_av = 0; } +AreaMap::AreaMap (const AreaMap &other) + : m_nx (0), m_ny (0) +{ + mp_av = 0; + operator= (other); +} + +AreaMap & +AreaMap::operator= (const AreaMap &other) +{ + if (this != &other) { + // TODO: this could be copy on write + reinitialize (other.p0 (), other.d (), other.p (), other.nx (), other.ny ()); + if (other.mp_av) { + memcpy (mp_av, other.mp_av, m_nx * m_ny * sizeof (*mp_av)); + } + } + return *this; +} + AreaMap::AreaMap (const db::Point &p0, const db::Vector &d, size_t nx, size_t ny) - : m_p0 (p0), m_d (d), m_nx (nx), m_ny (ny) + : m_p0 (p0), m_d (d), m_p (d), m_nx (nx), m_ny (ny) +{ + mp_av = new area_type [nx * ny]; + clear (); +} + +AreaMap::AreaMap (const db::Point &p0, const db::Vector &d, const db::Vector &p, size_t nx, size_t ny) + : m_p0 (p0), m_d (d), m_p (std::min (d.x (), p.x ()), std::min (d.y (), p.y ())), m_nx (nx), m_ny (ny) { mp_av = new area_type [nx * ny]; clear (); @@ -1580,9 +1607,16 @@ AreaMap::~AreaMap () void AreaMap::reinitialize (const db::Point &p0, const db::Vector &d, size_t nx, size_t ny) +{ + reinitialize (p0, d, d, nx, ny); +} + +void +AreaMap::reinitialize (const db::Point &p0, const db::Vector &d, const db::Vector &p, size_t nx, size_t ny) { m_p0 = p0; m_d = d; + m_p = db::Vector (std::min (d.x (), p.x ()), std::min (d.y (), p.y ())); m_nx = nx; m_ny = ny; @@ -1629,6 +1663,16 @@ AreaMap::total_area () const return asum; } +db::Box +AreaMap::bbox () const +{ + if (m_nx == 0 || m_ny == 0) { + return db::Box (); + } else { + return db::Box (m_p0, m_p0 + db::Vector (db::Coord (m_nx - 1) * m_d.x () + m_p.x (), db::Coord (m_ny - 1) * m_d.y () + m_p.y ())); + } +} + // ------------------------------------------------------------------------- // Implementation of rasterize @@ -1646,6 +1690,7 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am) db::Coord ymin = box.bottom (), ymax = box.top (); db::Coord dy = am.d ().y (), dx = am.d ().x (); + db::Coord py = am.p ().y (), px = am.p ().x (); db::Coord y0 = am.p0 ().y (), x0 = am.p0 ().x (); size_t ny = am.ny (), nx = am.nx (); @@ -1695,7 +1740,7 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am) for (size_t iy = iy0; iy < iy1; ++iy) { - db::Coord yy = y + dy; + db::Coord yy = y + py; while (f != edges.end () && db::edge_ymin (*f) < yy) { ++f; } @@ -1711,7 +1756,7 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am) while (cc != edges.end () && cc != f && db::edge_xmax (*cc) <= x) { db::Coord y1 = std::max (y, std::min (yy, cc->p1 ().y ())); db::Coord y2 = std::max (y, std::min (yy, cc->p2 ().y ())); - a += area_type (dx) * area_type (y2 - y1); + a += area_type (px) * area_type (y2 - y1); ++cc; } @@ -1719,7 +1764,8 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am) for (size_t ix = ix0; ix < ix1; ++ix) { - db::Coord xx = x + dx; + db::Coord xx = x + px; + db::Coord xxx = x + dx; // TODO: edge_xmin_at_interval(y, yy) and edge_xmax.. would be more efficient in the // all-angle case. However, it is crucial that the edge clipping produces @@ -1730,6 +1776,14 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am) ++ff; } + std::vector ::iterator fff = ff; + + if (xx < xxx) { + while (fff != f && db::edge_xmin (*fff) < xxx) { + ++fff; + } + } + if (xl < x) { // consider all edges or parts of those left of the first cell @@ -1739,24 +1793,50 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am) std::pair ec = e->clipped (left); if (ec.first && db::edge_xmin (ec.second) < x) { - a += area_type (ec.second.dy ()) * area_type (dx); + a += area_type (ec.second.dy ()) * area_type (px); } } } - db::Box cell (x, y, xx, yy); - area_type aa = a; - for (std::vector ::iterator e = cc; e != ff; ++e) { + if (dx == py) { - std::pair ec = e->clipped (cell); - if (ec.first && db::edge_xmin (ec.second) < xx) { + db::Box cell (x, y, xx, yy); - aa += area_type (ec.second.dy ()) * area_type (2 * xx - (ec.second.p2 ().x () + ec.second.p1 ().x ())) / 2; - a += area_type (ec.second.dy ()) * area_type (dx); + for (std::vector ::iterator e = cc; e != ff; ++e) { + + std::pair ec = e->clipped (cell); + if (ec.first && db::edge_xmin (ec.second) < xx) { + aa += (area_type (ec.second.dy ()) * area_type (2 * xx - (ec.second.p2 ().x () + ec.second.p1 ().x ()))) / 2; + a += area_type (ec.second.dy ()) * area_type (px); + } + + } + + } else { + + db::Box cell (x, y, xx, yy); + + for (std::vector ::iterator e = cc; e != ff; ++e) { + + std::pair ec = e->clipped (cell); + if (ec.first && db::edge_xmin (ec.second) < xx) { + aa += (area_type (ec.second.dy ()) * area_type (2 * xx - (ec.second.p2 ().x () + ec.second.p1 ().x ()))) / 2; + } + + } + + db::Box wide_cell (x, y, x + dx, yy); + + for (std::vector ::iterator e = cc; e != fff; ++e) { + + std::pair wide_ec = e->clipped (wide_cell); + if (wide_ec.first && db::edge_xmin (wide_ec.second) < x + dx) { + a += area_type (wide_ec.second.dy ()) * area_type (px); + } } @@ -1764,8 +1844,10 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am) am.get (ix, iy) += aa; - x = xx; - xl = xx; + x += dx; + xl = x; + + ff = fff; for (std::vector ::iterator ccx = cc; ccx != ff; ++ccx) { if (db::edge_xmax (*ccx) <= x) { @@ -1776,6 +1858,13 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am) } + if (yy < y + dy) { + yy = y + dy; + while (f != edges.end () && db::edge_ymin (*f) < yy) { + ++f; + } + } + y = yy; for (std::vector ::iterator cx = c; cx != f; ++cx) { diff --git a/src/db/db/dbPolygonTools.h b/src/db/db/dbPolygonTools.h index 26f963039..423be8216 100644 --- a/src/db/db/dbPolygonTools.h +++ b/src/db/db/dbPolygonTools.h @@ -500,21 +500,41 @@ public: */ AreaMap (); + /** + * @brief Copy constructor + */ + AreaMap (const AreaMap &); + /** * @brief Constructor */ AreaMap (const db::Point &p0, const db::Vector &d, size_t nx, size_t ny); + /** + * @brief Constructor with pixel size + */ + AreaMap (const db::Point &p0, const db::Vector &d, const db::Vector &p, size_t nx, size_t ny); + /** * @brief Destructor */ ~AreaMap (); + /** + * @brief Assignment + */ + AreaMap &operator= (const AreaMap &); + /** * @brief Reinitialize */ void reinitialize (const db::Point &p0, const db::Vector &d, size_t nx, size_t ny); + /** + * @brief Reinitialize with pixel size + */ + void reinitialize (const db::Point &p0, const db::Vector &d, const db::Vector &p, size_t nx, size_t ny); + /** * @brief Swap of two maps */ @@ -576,13 +596,18 @@ public: return m_d; } + /** + * @brief The pixel size (must be less than d) + */ + const db::Vector &p () const + { + return m_p; + } + /** * @brief Compute the bounding box of the area map */ - db::Box bbox () const - { - return db::Box (m_p0, m_p0 + db::Vector (db::Coord (m_nx) * m_d.x (), db::Coord (m_ny) * m_d.y ())); - } + db::Box bbox () const; /** * @brief Compute the total area @@ -594,7 +619,7 @@ public: */ area_type pixel_area () const { - return area_type (m_d.x ()) * area_type (m_d.y ()); + return area_type (m_p.x ()) * area_type (m_p.y ()); } /** @@ -606,11 +631,8 @@ private: area_type *mp_av; db::Point m_p0; db::Vector m_d; + db::Vector m_p; size_t m_nx, m_ny; - - // no copying - AreaMap (const AreaMap &); - AreaMap &operator= (const AreaMap &); }; /** diff --git a/src/db/unit_tests/dbPolygonToolsTests.cc b/src/db/unit_tests/dbPolygonToolsTests.cc index 156aa6a8d..147aaf22c 100644 --- a/src/db/unit_tests/dbPolygonToolsTests.cc +++ b/src/db/unit_tests/dbPolygonToolsTests.cc @@ -918,6 +918,37 @@ TEST(25) EXPECT_EQ (am_to_string (am), "(1650,5000,8350,3000,0),(3350,9175,10000,8660,330),(0,825,6650,10000,5000),(0,0,0,3350,8845),(0,0,0,0,825)"); EXPECT_EQ (am.total_area (), 85010); + + am.reinitialize (db::Point (0, 0), db::Vector (100, 100), db::Vector (50, 50), 5, 5); + db::rasterize (in, am); + + EXPECT_EQ (am_to_string (am), "(0,0,1250,750,0),(825,2500,2500,2500,0),(0,0,2287,2500,1750),(0,0,0,825,2500),(0,0,0,0,0)"); + EXPECT_EQ (am.total_area (), 20187); + + am.reinitialize (db::Point (200, 0), db::Vector (100, 100), db::Vector (50, 50), 1, 1); + db::rasterize (in, am); + + EXPECT_EQ (am_to_string (am), "(1250)"); + + am.reinitialize (db::Point (300, 0), db::Vector (100, 100), db::Vector (50, 50), 1, 1); + db::rasterize (in, am); + + EXPECT_EQ (am_to_string (am), "(750)"); + + am.reinitialize (db::Point (400, 0), db::Vector (100, 100), db::Vector (50, 50), 1, 1); + db::rasterize (in, am); + + EXPECT_EQ (am_to_string (am), "(0)"); + + am.reinitialize (db::Point (400, 100), db::Vector (100, 100), db::Vector (50, 50), 1, 1); + db::rasterize (in, am); + + EXPECT_EQ (am_to_string (am), "(0)"); + + am.reinitialize (db::Point (400, 200), db::Vector (100, 100), db::Vector (50, 50), 1, 1); + db::rasterize (in, am); + + EXPECT_EQ (am_to_string (am), "(1750)"); } TEST(26) @@ -1047,7 +1078,7 @@ TEST(30) EXPECT_EQ (am_to_string (am), "(5418,1071,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),(6642,6267,1967,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),(6642,6642,6582,3119,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),(6642,6642,6642,6642,4317,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),(1701,1701,1701,4995,6642,5303,735,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),(0,0,0,4428,6642,6642,5952,1127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),(0,0,0,4428,6642,6642,6642,6178,1484,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),(0,0,0,2430,3645,3645,4644,6642,6355,1859,0,0,0,0,0,0,0,0,0,0,0,0,0,0),(0,0,0,0,0,0,2214,6642,6642,6489,2275,0,0,0,0,0,0,0,0,0,0,0,0,0),(0,0,0,0,0,0,2214,6642,6642,6642,6582,2698,0,0,0,0,0,0,0,0,0,0,0,0),(0,0,0,0,0,0,1863,5589,5589,5589,6642,6632,2994,0,0,0,0,0,0,0,0,0,0,0),(0,0,0,0,0,0,0,0,0,0,6642,6642,6624,2698,0,0,0,0,0,0,0,0,0,0),(0,0,0,0,0,0,0,0,0,0,6642,6642,6642,6587,2379,0,0,0,0,0,0,0,0,0),(0,0,0,0,0,0,0,0,0,0,6642,6642,6642,6642,6537,2111,0,0,0,0,0,0,0,0),(0,0,0,0,0,0,0,0,0,0,891,891,891,4725,6642,6471,1859,0,0,0,0,0,0,0),(0,0,0,0,0,0,0,0,0,0,0,0,0,4428,6642,6642,6378,1570,0,0,0,0,0,0),(0,0,0,0,0,0,0,0,0,0,0,0,0,4428,6642,6642,6642,6166,960,0,0,0,0,0),(0,0,0,0,0,0,0,0,0,0,0,0,0,1890,2835,2835,4104,6642,5617,432,0,0,0,0),(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2214,6642,6642,4860,104,0,0,0),(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2214,6642,6642,6642,3854,0,0,0),(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1593,4779,4779,4779,6642,2788,0,0),(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6642,6538,1777,0),(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6642,6642,6110,704),(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6642,6642,6642,4539),(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,81,81,81,55)"); } -TEST(31) +TEST(41) { db::Point pattern [] = { db::Point (0, -100), @@ -1131,7 +1162,7 @@ TEST(31) EXPECT_EQ (pout.to_string (), "(10,-90;10,-65;-90,-65;-90,235;10,410;510,410;535,385;610,385;610,-40;510,-90)"); } -TEST(32) +TEST(42) { db::Point pattern [] = { db::Point (0, -100),