diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index b793391c6..733f8ec3e 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -955,7 +955,7 @@ DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout // collects the cell mappings we skip because they are variants (variant building or box variants) std::map cm_skipped_variants; - if (into_layout == original_builder.source ().layout () && &into_layout->cell (into_cell) == original_builder.source ().top_cell ()) { + if (into_layout == original_builder.source ().layout () && &into_layout->cell (into_cell) == original_builder.source ().top_cell () && original_builder.source ().global_trans ().is_unity ()) { // This is the case of mapping back to the original. In this case we can use the information // provided inside the original hierarchy builders. They list the source cells and the target cells diff --git a/src/db/db/dbEdgePairs.cc b/src/db/db/dbEdgePairs.cc index 3c5e65d5c..9412eb815 100644 --- a/src/db/db/dbEdgePairs.cc +++ b/src/db/db/dbEdgePairs.cc @@ -139,6 +139,8 @@ EdgePairs &EdgePairs::transform (const T &trans) template DB_PUBLIC EdgePairs &EdgePairs::transform (const db::ICplxTrans &); template DB_PUBLIC EdgePairs &EdgePairs::transform (const db::Trans &); template DB_PUBLIC EdgePairs &EdgePairs::transform (const db::Disp &); +template DB_PUBLIC EdgePairs &EdgePairs::transform (const db::IMatrix2d &); +template DB_PUBLIC EdgePairs &EdgePairs::transform (const db::IMatrix3d &); const db::RecursiveShapeIterator & EdgePairs::iter () const diff --git a/src/db/db/dbEdges.cc b/src/db/db/dbEdges.cc index 8143070c1..862b1f9ca 100644 --- a/src/db/db/dbEdges.cc +++ b/src/db/db/dbEdges.cc @@ -184,6 +184,8 @@ Edges &Edges::transform (const T &trans) template DB_PUBLIC Edges &Edges::transform (const db::ICplxTrans &); template DB_PUBLIC Edges &Edges::transform (const db::Trans &); template DB_PUBLIC Edges &Edges::transform (const db::Disp &); +template DB_PUBLIC Edges &Edges::transform (const db::IMatrix2d &); +template DB_PUBLIC Edges &Edges::transform (const db::IMatrix3d &); template void Edges::insert (const Sh &shape) diff --git a/src/db/db/dbFillTool.cc b/src/db/db/dbFillTool.cc index 2fe2a7489..855be54b5 100644 --- a/src/db/db/dbFillTool.cc +++ b/src/db/db/dbFillTool.cc @@ -26,366 +26,199 @@ #include "dbEdgeProcessor.h" #include "dbRegion.h" #include "dbCell.h" +#include "dbTilingProcessor.h" #include "tlIntervalMap.h" +#include "tlMath.h" namespace db { -namespace +class GenericRasterizer { - struct AddJoinOperator +public: + GenericRasterizer () + : m_row_step (), m_column_step (), m_row_steps (0), m_column_steps (0), m_origin (), m_dim () { - void operator() (unsigned int &a, unsigned int b) - { - a += b; - } - }; -} - -static db::Vector -optimize_offset (const db::Polygon &fp, const db::AreaMap &am) -{ - db::Coord xshift = 0, yshift = 0; + // .. nothing yet .. + } + GenericRasterizer (const db::Polygon &fp, const db::Vector &row_step, const db::Vector &column_step, const db::Point &origin, const db::Vector &dim) + : m_row_step (row_step), m_column_step (column_step), m_row_steps (0), m_column_steps (0), m_origin (origin), m_dim (dim) { - - tl::interval_map voting; - AddJoinOperator op; - - db::AreaMap::area_type amax = db::AreaMap::area_type (am.d ().x ()) * db::AreaMap::area_type (am.d ().y ()); - - db::Coord dx = am.d ().x (); - db::Coord dy = am.d ().y (); - size_t nx = am.nx (); - size_t ny = am.ny (); - - // Derive a optimal new x offset from the mapping - for (size_t j = 0; j < size_t (ny); ++j) { - - bool x1set = false; - bool x2set = false; - db::Coord x1 = 0; - db::Coord x2 = 0; - - for (size_t i = 0; i < size_t (nx); ++i) { - - if (am.get (i, j) >= amax) { - - if (! x1set) { - - x1 = 0; - x1set = true; - - } else if (x2set) { - - x1 = x2; - x1set = true; - x2set = false; - - } - - } else if (am.get (i, j) > 0) { - - if (! x1set || x2set) { - - x1 = db::Coord (am.get (i, j) / dy); - x1set = true; - x2set = false; - - } else if (! x2set) { - - x2 = db::Coord (am.get (i, j) / dy); - x2set = true; - - } - - } else if (am.get (i, j) == 0) { - - if (x1set) { - - if (! x2set) { - x2 = 0; - } - - if (x1 + x2 < dx) { - voting.add (-x1, x2 + 1, (unsigned int) 1, op); - } else { - voting.add (-x1, x2 - dx + 1, (unsigned int) 1, op); - voting.add (dx - x1, x2 + 1, (unsigned int) 1, op); - } - - x1set = false; - x2set = false; - - } - - } - - } - - if (x1set) { - - if (! x2set) { - x2 = 0; - } - - if (x1 + x2 < dx) { - voting.add (-x1, x2 + 1, (unsigned int) 1, op); - } else { - voting.add (-x1, x2 - dx + 1, (unsigned int) 1, op); - voting.add (dx - x1, x2 + 1, (unsigned int) 1, op); - } - - } - - } - - std::set xshifts; - for (db::Polygon::polygon_edge_iterator e = fp.begin_edge (); ! e.at_end (); ++e) { - xshifts.insert (((*e).p1 ().x () - am.p0 ().x ()) % dx); - } - - unsigned int max_votes = 0; - for (std::set ::const_iterator xs = xshifts.begin (); xs != xshifts.end (); ++xs) { - const unsigned int *z = voting.mapped (*xs); - if (z && *z > max_votes) { - xshift = *xs; - max_votes = *z; - } - } - + rasterize (fp); } + void move (const db::Vector &d) { + m_origin += d; + clear (); + } - tl::interval_map voting; - AddJoinOperator op; + void clear () + { + m_area_maps.clear (); + } - db::AreaMap::area_type amax = db::AreaMap::area_type (am.d ().x ()) * db::AreaMap::area_type (am.d ().y ()); + void rasterize (const db::Polygon &fp) + { + db::Coord dx = m_row_step.x (); + db::Coord dy = m_column_step.y (); - db::Coord dx = am.d ().x (); - db::Coord dy = am.d ().y (); - size_t nx = am.nx (); - size_t ny = am.ny (); + if (m_row_step.y () == 0) { + m_row_steps = 1; + } else { + m_row_steps = tl::lcm (dy, std::abs (m_row_step.y ())) / std::abs (m_row_step.y ()); + } - // Derive a optimal new y offset from the mapping - for (size_t i = 0; i < size_t (nx); ++i) { + if (m_column_step.x () == 0) { + m_column_steps = 1; + } else { + m_column_steps = tl::lcm (dx, std::abs (m_column_step.x ())) / std::abs (m_column_step.x ()); + } - bool y1set = false; - bool y2set = false; - db::Coord y1 = 0; - db::Coord y2 = 0; + // because the rasterizer can't handle overlapping cells we need to multiply the row and columns steps + // with an integer until the effective rasterizer pitch get big enough. + m_row_steps *= (m_dim.x () - 1) / (m_row_steps * m_row_step.x ()) + 1; + m_column_steps *= (m_dim.y () - 1) / (m_column_steps * m_column_step.y ()) + 1; - for (size_t j = 0; j < size_t (ny); ++j) { + db::Box fp_bbox = fp.box (); - if (am.get (i, j) >= amax) { + // compensate for distortion by sheared kernel + fp_bbox.enlarge (db::Vector (db::coord_traits::rounded (double (fp_bbox.height ()) * std::abs (m_column_step.x ()) / dy), db::coord_traits::rounded (double (fp_bbox.width ()) * std::abs (m_row_step.y ()) / dx))); - if (! y1set) { + int columns_per_rows = (int (m_row_steps) * m_row_step.y ()) / dy; + int rows_per_columns = (int (m_column_steps) * m_column_step.x ()) / dx; - y1 = 0; - y1set = true; + db::Coord ddx = dx * db::Coord (m_row_steps) - m_column_step.x () * columns_per_rows; + db::Coord ddy = dy * db::Coord (m_column_steps) - m_row_step.y () * rows_per_columns; - } else if (y2set) { + // round polygon bbox + db::Coord fp_left = db::Coord (tl::round_down (fp_bbox.left () - m_origin.x (), ddx)) + m_origin.x (); + db::Coord fp_bottom = db::Coord (tl::round_down (fp_bbox.bottom () - m_origin.y (), ddy)) + m_origin.y (); + db::Coord fp_right = db::Coord (tl::round_up (fp_bbox.right () - m_origin.x (), ddx)) + m_origin.x (); + db::Coord fp_top = db::Coord (tl::round_up (fp_bbox.top () - m_origin.y (), ddy)) + m_origin.y (); + fp_bbox = db::Box (fp_left, fp_bottom, fp_right, fp_top); - y1 = y2; - y1set = true; - y2set = false; + size_t nx = fp_bbox.width () / ddx; + size_t ny = fp_bbox.height () / ddy; - } + tl_assert (fp.box ().inside (fp_bbox)); - } else if (am.get (i, j) > 0) { + if (nx == 0 || ny == 0) { + // nothing to rasterize: + return; + } - if (! y1set || y2set) { + m_area_maps.reserve (m_row_steps * m_column_steps + std::abs (columns_per_rows) * std::abs (rows_per_columns)); - y1 = db::Coord (am.get (i, j) / dx); - y1set = true; - y2set = false; + db::AreaMap am; - } else if (! y2set) { + for (unsigned int ic = 0; ic < m_column_steps; ++ic) { - y2 = db::Coord (am.get (i, j) / dx); - y2set = true; + 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); - } else if (am.get (i, j) == 0) { + am.reinitialize (db::Point (fp_left, fp_bottom) + dr + dc, db::Vector (ddx, ddy), m_dim, nx, ny); - if (y1set) { - - if (! y2set) { - y2 = 0; - } - - if (y1 + y2 < dy) { - voting.add (-y1, y2 + 1, (unsigned int) 1, op); - } else { - voting.add (-y1, y2 - dy + 1, (unsigned int) 1, op); - voting.add (dy - y1, y2 + 1, (unsigned int) 1, op); - } - - y1set = false; - y2set = false; - - } - - } - - } - - if (y1set) { - - if (! y2set) { - y2 = 0; - } - - if (y1 + y2 < dy) { - voting.add (-y1, y2 + 1, (unsigned int) 1, op); - } else { - voting.add (-y1, y2 - dy + 1, (unsigned int) 1, op); - voting.add (dy - y1, y2 + 1, (unsigned int) 1, op); + if (db::rasterize (fp, am)) { + m_area_maps.push_back (db::AreaMap ()); + m_area_maps.back ().swap (am); } } } - std::set yshifts; - for (db::Polygon::polygon_edge_iterator e = fp.begin_edge (); ! e.at_end (); ++e) { - yshifts.insert (((*e).p1 ().y () - am.p0 ().y ()) % dy); - } + // adds the "dead corner" piece - unsigned int max_votes = 0; - for (std::set ::const_iterator ys = yshifts.begin (); ys != yshifts.end (); ++ys) { - const unsigned int *z = voting.mapped (*ys); - if (z && *z > max_votes) { - yshift = *ys; - max_votes = *z; - } - } + for (unsigned int ic = 0; ic < (unsigned int) std::abs (columns_per_rows); ++ic) { - } + for (unsigned int ir = 0; ir < (unsigned int) std::abs (rows_per_columns); ++ir) { - return db::Vector (xshift, yshift); -} + db::Vector dr = m_row_step * long ((rows_per_columns > 0 ? -(ir + 1) : ir) + m_row_steps); + db::Vector dc = m_column_step * long ((columns_per_rows > 0 ? -(ic + 1) : ic) + m_column_steps); -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 (); + am.reinitialize (db::Point (fp_left, fp_bottom) + dr + dc, db::Vector (ddx, ddy), m_dim, nx, ny); - if (tl::verbosity () >= 50) { - tl::info << "Simple rasterize polygon: " << fp.to_string () << " with box " << fc_bbox.to_string (); - } - - db::Box fp_bbox = fp.box (); - - // round polygon bbox - db::Coord fp_left = dx * ((fp_bbox.left () - p0.x ()) / dx) + p0.x (); - db::Coord fp_bottom = dy * ((fp_bbox.bottom () - p0.y ()) / dy) + p0.y (); - db::Coord fp_right = dx * ((fp_bbox.right () + dx - 1 - p0.x ()) / dx) + p0.x (); - db::Coord fp_top = dy * ((fp_bbox.top () + dy - 1 - p0.y ()) / dy) + p0.y (); - fp_bbox = db::Box (fp_left, fp_bottom, fp_right, fp_top); - - db::Coord nx = fp_bbox.width () / dx; - db::Coord ny = fp_bbox.height () / dy; - - if (nx <= 0 || ny <= 0) { - // nothing to rasterize: - return false; - } - - am.reinitialize (fp_bbox.p1 (), db::Vector (dx, dy), size_t (nx), size_t (ny)); - - // Rasterize to determine fill regions - db::rasterize (fp, am); - - return true; -} - -static bool -rasterize_extended (const db::Polygon &fp, const db::Box &fc_bbox, db::AreaMap &am) -{ - db::Coord dx = fc_bbox.width (); - db::Coord dy = fc_bbox.height (); - - if (tl::verbosity () >= 50) { - tl::info << "Optimized rasterize polygon: " << fp.to_string () << " with box " << fc_bbox.to_string (); - } - - db::Box fp_bbox = fp.box (); - - db::Coord nx = (fp_bbox.width () + dx - 1) / dx; - db::Coord ny = (fp_bbox.height () + dy - 1) / dy; - - if (nx <= 0 || ny <= 0) { - // nothing to rasterize: - return false; - } - - am.reinitialize (fp_bbox.p1 (), db::Vector (dx, dy), size_t (nx), size_t (ny)); - - // Rasterize to determine fill regions - db::rasterize (fp, am); - - if (tl::verbosity () >= 50) { - - db::Coord nx = db::Coord (am.nx ()); - db::Coord ny = db::Coord (am.ny ()); - db::AreaMap::area_type amax = am.pixel_area (); - double n = 0; - for (size_t i = 0; i < size_t (nx); ++i) { - for (size_t j = 0; j < size_t (ny); ++j) { - if (am.get (i, j) >= amax) { - n += 1; + if (db::rasterize (fp, am)) { + m_area_maps.push_back (db::AreaMap ()); + m_area_maps.back ().swap (am); } + } + } - - tl::info << "Number of fill regions before optimization: " << n; - } - db::Vector d = optimize_offset (fp, am); + size_t filled_pixels () const + { + size_t n = 0; - if (tl::verbosity () >= 50) { - tl::info << "Shift vector: " << d.to_string (); - } + for (std::vector::const_iterator a = m_area_maps.begin (); a != m_area_maps.end (); ++a) { - if (d.x () != 0 || d.y () != 0) { + db::Coord nx = db::Coord (a->nx ()); + db::Coord ny = db::Coord (a->ny ()); + db::AreaMap::area_type amax = a->pixel_area (); - am.move (d); - am.clear (); - - db::rasterize (fp, am); - - if (tl::verbosity () >= 50) { - - db::Coord nx = db::Coord (am.nx ()); - db::Coord ny = db::Coord (am.ny ()); - db::AreaMap::area_type amax = am.pixel_area (); double n = 0; for (size_t i = 0; i < size_t (nx); ++i) { for (size_t j = 0; j < size_t (ny); ++j) { - if (am.get (i, j) >= amax) { + if (a->get (i, j) >= amax) { n += 1; } } } - tl::info << "Number of fill regions after optimization: " << n; - } + return n; } - return true; -} + const db::Point &p0 () const { return m_origin; } -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) + unsigned int row_steps () const { return m_row_steps; } + unsigned int column_steps () const { return m_column_steps; } + + unsigned int area_maps () const + { + return m_area_maps.size (); + } + + const db::AreaMap &area_map (unsigned int i) const + { + return m_area_maps [i]; + } + +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; + db::Vector m_dim; +}; + + +static bool +fill_polygon_impl (db::Cell *cell, const db::Polygon &fp0, db::cell_index_type fill_cell_index, const db::Box &fc_bbox, 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, const db::Box &glue_box) { + if (row_step.x () <= 0 || column_step.y () <= 0) { + throw tl::Exception (tl::to_string (tr ("Invalid row or column step vectors in fill_region: row step must have a positive x component while column step must have a positive y component"))); + } + + if (db::vprod_sign (row_step, column_step) <= 0) { + 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"))); + } + + // disable enhanced mode an obey the origin if the polygon is not entirely inside and not at the boundary of the glue box + if (enhanced_fill && ! glue_box.empty () && ! fp0.box ().enlarged (db::Vector (1, 1)).inside (glue_box)) { + enhanced_fill = false; + } + + db::Vector kernel_origin (fc_bbox.left (), fc_bbox.bottom ()); + std::vector filled_regions; db::EdgeProcessor ep; @@ -419,17 +252,25 @@ fill_region (db::Cell *cell, const db::Polygon &fp0, db::cell_index_type fill_ce for (std::vector ::const_iterator fp = fpb.begin (); fp != fpb.end (); ++fp) { + if (fp->hull ().size () == 0) { + continue; + } + size_t ninsts = 0; - db::AreaMap am; + db::Point o = origin; + if (enhanced_fill) { + o = fp->hull () [0]; + } - // Rasterize to determine fill regions - if ((enhanced_fill && rasterize_extended (*fp, fc_bbox, am)) || (!enhanced_fill && rasterize_simple (*fp, fc_bbox, origin, am))) { + GenericRasterizer am (*fp, row_step, column_step, o, fc_bbox.p2 () - fc_bbox.p1 ()); - size_t nx = am.nx (); - size_t ny = am.ny (); + for (unsigned int i = 0; i < am.area_maps (); ++i) { - db::AreaMap::area_type amax = am.pixel_area (); + const db::AreaMap &am1 = am.area_map (i); + + size_t nx = am1.nx (); + size_t ny = am1.ny (); // Create the fill cell instances for (size_t i = 0; i < nx; ++i) { @@ -437,31 +278,41 @@ fill_region (db::Cell *cell, const db::Polygon &fp0, db::cell_index_type fill_ce for (size_t j = 0; j < ny; ) { size_t jj = j + 1; - if (am.get (i, j) >= amax) { + if (am1.get (i, j) == am1.pixel_area ()) { - while (jj != ny && am.get (i, jj) >= amax) { + while (jj != ny && am1.get (i, jj) == am1.pixel_area ()) { ++jj; } ninsts += (jj - j); - db::Vector p0 (am.p0 () - fc_bbox.p1 ()); - p0 += db::Vector (db::Coord (i) * fc_bbox.width (), db::Coord (j) * fc_bbox.height ()); + 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, fc_bbox.height ()), db::Vector (fc_bbox.width (), 0), (unsigned long) (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); + { + // In case we run this from a tiling processor we need to lock against multithread races + tl::MutexLocker locker (&db::TilingProcessor::output_lock ()); + cell->insert (array); + } if (remaining_parts) { - db::Box filled_box = array.raw_bbox () * fc_bbox; - filled_regions.push_back (db::Polygon (filled_box.enlarged (fill_margin))); + 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; } @@ -484,9 +335,19 @@ fill_region (db::Cell *cell, const db::Polygon &fp0, db::cell_index_type fill_ce if (any_fill) { if (remaining_parts) { + std::vector fp1; + + if (fill_margin != db::Vector ()) { + ep.size (filled_regions, fill_margin.x (), fill_margin.y (), fp1, 3 /*mode*/, false /*=don't resolve holes*/); + filled_regions.swap (fp1); + fp1.clear (); + } + fp1.push_back (fp0); ep.boolean (fp1, filled_regions, *remaining_parts, db::BooleanOp::ANotB, false /*=don't resolve holes*/); + + } return true; @@ -496,17 +357,59 @@ fill_region (db::Cell *cell, const db::Polygon &fp0, db::cell_index_type fill_ce } } -DB_PUBLIC void -fill_region (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Point &origin, bool enhanced_fill, - db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons) +DB_PUBLIC bool +fill_region (db::Cell *cell, const db::Polygon &fp0, db::cell_index_type fill_cell_index, const Box &fc_box, 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, const db::Box &glue_box) { + return fill_polygon_impl (cell, fp0, fill_cell_index, fc_box, row_step, column_step, origin, enhanced_fill, remaining_parts, fill_margin, glue_box); +} + +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, const db::Box &glue_box) +{ + if (fc_bbox.empty () || fc_bbox.width () == 0 || fc_bbox.height () == 0) { + throw tl::Exception (tl::to_string (tr ("Invalid fill cell footprint (empty or zero width/height)"))); + } + + return fill_polygon_impl (cell, fp0, fill_cell_index, fc_bbox, db::Vector (fc_bbox.width (), 0), db::Vector (0, fc_bbox.height ()), origin, enhanced_fill, remaining_parts, fill_margin, glue_box); +} + +static void +fill_region_impl (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index, const db::Box &fc_bbox, const db::Vector &row_step, const db::Vector &column_step, const db::Point &origin, bool enhanced_fill, + db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, int iteration, const db::Box &glue_box) +{ + if (row_step.x () <= 0 || column_step.y () <= 0) { + throw tl::Exception (tl::to_string (tr ("Invalid row or column step vectors in fill_region: row step must have a positive x component while column step must have a positive y component"))); + } + + if (db::vprod_sign (row_step, column_step) <= 0) { + 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"))); + } + std::vector rem_pp, rem_poly; + size_t n = 0; for (db::Region::const_iterator p = fr.begin_merged (); !p.at_end (); ++p) { - if (!fill_region (cell, *p, fill_cell_index, fc_box, origin, enhanced_fill, remaining_parts ? &rem_pp : 0, fill_margin)) { - if (remaining_polygons) { - rem_poly.push_back (*p); + ++n; + } + + { + std::string progress_title; + if (iteration > 0) { + progress_title = tl::sprintf (tl::to_string (tr ("Fill polygons (iteration #%d)")), iteration); + } else { + progress_title = tl::sprintf (tl::to_string (tr ("Fill polygons"))); + } + tl::RelativeProgress progress (progress_title, n); + + for (db::Region::const_iterator p = fr.begin_merged (); !p.at_end (); ++p) { + if (! fill_polygon_impl (cell, *p, fill_cell_index, fc_bbox, row_step, column_step, origin, enhanced_fill, remaining_parts ? &rem_pp : 0, fill_margin, glue_box)) { + if (remaining_polygons) { + rem_poly.push_back (*p); + } } + ++progress; } } @@ -529,4 +432,48 @@ fill_region (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell } } +DB_PUBLIC void +fill_region (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index, const db::Box &fc_bbox, const db::Vector &row_step, const db::Vector &column_step, const db::Point &origin, bool enhanced_fill, + db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box) +{ + fill_region_impl (cell, fr, fill_cell_index, fc_bbox, row_step, column_step, origin, enhanced_fill, remaining_parts, fill_margin, remaining_polygons, 0, glue_box); +} + +DB_PUBLIC void +fill_region (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index, const db::Box &fc_bbox, const db::Point &origin, bool enhanced_fill, + db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box) +{ + if (fc_bbox.empty () || fc_bbox.width () == 0 || fc_bbox.height () == 0) { + throw tl::Exception (tl::to_string (tr ("Invalid fill cell footprint (empty or zero width/height)"))); + } + + fill_region_impl (cell, fr, fill_cell_index, fc_bbox, db::Vector (fc_bbox.width (), 0), db::Vector (0, fc_bbox.height ()), + origin, enhanced_fill, remaining_parts, fill_margin, remaining_polygons, 0, glue_box); +} + +DB_PUBLIC void +fill_region_repeat (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index, + const db::Box &fc_box, const db::Vector &row_step, const db::Vector &column_step, + const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Point &origin, const db::Box &glue_box) +{ + const db::Region *fill_region = &fr; + + db::Region new_fill_region; + db::Region remaining; + + int iteration = 0; + + while (! fill_region->empty ()) { + + ++iteration; + + remaining.clear (); + fill_region_impl (cell, *fill_region, fill_cell_index, fc_box, row_step, column_step, origin, true, &remaining, fill_margin, remaining_polygons, iteration, glue_box); + + new_fill_region.swap (remaining); + fill_region = &new_fill_region; + + } +} + } diff --git a/src/db/db/dbFillTool.h b/src/db/db/dbFillTool.h index 64407e4df..a6b5c905c 100644 --- a/src/db/db/dbFillTool.h +++ b/src/db/db/dbFillTool.h @@ -37,6 +37,8 @@ class Region; * @param fp0 The polygon to fill. Ideally, this polygon is merged and does not overlap with any other polygons. * @param fill_cell_index The index of the cell to use for tiling * @param fc_bbox The fill cell's footprint box. The footprint gives the area covered by one instance of the tiling cell. + * @param row_step (some_versions) The row advance vector of the fill cell. By default this is (fc_bbox.width(), 0) + * @param column_step (some_versions) The column advance vector of the fill cell. By default this is (0, fc_bbox.height()) * @param origin Specifies the origin of the fill raster if enhanced_fill is false * @param enhanced_fill If set, the tiling offset will be optimized such that as much tiling cells fit into each polygon * @@ -44,13 +46,45 @@ class Region; * * @param remaining_parts If non-null, this vector receives the parts of the polygons not covered by the tiling cells (plus the fill_margin) * @param fill_margin Only used if remaining_parts is not 0 (see there) + * @param glue_box Guarantees boundary compatibility * * Return value: true, if the polygon could be filled, false if no fill tile at all could be applied (remaining_parts will not be fed in that case) + * + * Explanation for the fill fc_box, row step and column step vectors: + * + * The "fc_box" is a rectangular area which is repeated along the primary fill axes given by row_step + * and column_step vectors. The fill box is placed with the lower-left corner. + * + * Formally, the fill box will be placed a positions + * + * p(i,j) = p0 + i * row_step + j * column_step + * + * p0 is a position chosen by the fill alogorithm or the "origin", if enhanced_fill is false. + * + * This pattern is overlaid with the polygon to fill and all instances where the fill box moved by p(i,j) is entirely inside + * the polygon generate a fill cell instance with a displacement of p. + * + * Afterwards, the residual parts are computed by subtracting all moved fill boxes from the polygon to fill. + * This implies that ideally the fc_boxes should overlap while they are repeated with row_step and column_step. + * + * As a practical consequence, if all fill cell geometries are within the fill boxes boundary, they will also + * be within the polygon to fill. + * + * If the glue box is non-empty, fill cells are guaranteed to use the global origin even in enhanced mode if + * unless they are entirely inside and not touching the boundary of the glue box. + * The glue box is useful to put the fill algorithm inside a tiling processor. In this case, the glue box + * is the tile box while the actual fill region can be larger to allow overlapping tiles. + * + * In enhanced fill mode, the origin is ignored unless a glue box is given. */ DB_PUBLIC bool fill_region (db::Cell *cell, const db::Polygon &fp, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Point &origin, bool enhanced_fill, - std::vector *remaining_parts = 0, const db::Vector &fill_margin = db::Vector ()); + std::vector *remaining_parts = 0, const db::Vector &fill_margin = db::Vector (), const db::Box &glue_box = db::Box ()); + +DB_PUBLIC bool +fill_region (db::Cell *cell, const db::Polygon &fp, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Vector &row_step, const db::Vector &column_step, const db::Point &origin, bool enhanced_fill, + std::vector *remaining_parts = 0, const db::Vector &fill_margin = db::Vector (), const db::Box &glue_box = db::Box ()); /** @@ -59,11 +93,31 @@ fill_region (db::Cell *cell, const db::Polygon &fp, db::cell_index_type fill_cel * remaining_parts (if non-null) will receive the non-filled parts of partially filled polygons. * fill_margin will specify the margin around the filled area when computing (through subtraction of the tiled area) the remaining_parts. * remaining_polygons (if non-null) will receive the polygons which could not be filled at all. + * + * In enhanced fill mode, the origin is ignored unless a glue box is given. */ DB_PUBLIC void fill_region (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Point &origin, bool enhanced_fill, - db::Region *remaining_parts = 0, const db::Vector &fill_margin = db::Vector (), db::Region *remaining_polygons = 0); + db::Region *remaining_parts = 0, const db::Vector &fill_margin = db::Vector (), db::Region *remaining_polygons = 0, const db::Box &glue_box = db::Box ()); + +DB_PUBLIC void +fill_region (db::Cell *cell, const db::Region &fp, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Vector &row_step, const db::Vector &column_step, const db::Point &origin, bool enhanced_fill, + db::Region *remaining_parts = 0, const db::Vector &fill_margin = db::Vector (), db::Region *remaining_polygons = 0, const db::Box &glue_box = db::Box ()); + +/** + * @brief An iterative version for enhanced fill + * + * This version operates like the region-based fill_region version, but repeates the fill step until no further fill cells can be placed. + * The remaining parts will be placed inside "remaining_polygons" unless this pointer is null. + * + * This version implies enhanced_mode (see "fill_region"). + * + * The origin is ignored unless a glue box is given. + */ +DB_PUBLIC void +fill_region_repeat (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index, + const db::Box &fc_box, const db::Vector &row_step, const db::Vector &column_step, + const db::Vector &fill_margin, db::Region *remaining_polygons = 0, const db::Point &origin = db::Point (), const db::Box &glue_box = db::Box ()); } - diff --git a/src/db/db/dbHierarchyBuilder.cc b/src/db/db/dbHierarchyBuilder.cc index 5a6751f87..838e93243 100644 --- a/src/db/db/dbHierarchyBuilder.cc +++ b/src/db/db/dbHierarchyBuilder.cc @@ -40,10 +40,10 @@ int compare_iterators_with_respect_to_target_hierarchy (const db::RecursiveShapeIterator &iter1, const db::RecursiveShapeIterator &iter2) { if ((iter1.layout () == 0) != (iter2.layout () == 0)) { - return (iter1.layout () == 0) < (iter2.layout () == 0); + return (iter1.layout () == 0) < (iter2.layout () == 0) ? -1 : 1; } if ((iter1.top_cell () == 0) != (iter2.top_cell () == 0)) { - return (iter1.top_cell () == 0) < (iter2.top_cell () == 0); + return (iter1.top_cell () == 0) < (iter2.top_cell () == 0) ? -1 : 1; } // basic source (layout, top_cell) needs to be the same of course @@ -70,6 +70,11 @@ compare_iterators_with_respect_to_target_hierarchy (const db::RecursiveShapeIter return iter1.enables () < iter2.enables () ? -1 : 1; } + // compare global transformations + if (! iter1.global_trans ().equal (iter2.global_trans ())) { + return iter1.global_trans ().less (iter2.global_trans ()) ? -1 : 1; + } + // if a region is set, the hierarchical appearance is the same only if the layers and // complex region are identical if ((iter1.region () == db::Box::world ()) != (iter2.region () == db::Box::world ())) { @@ -338,7 +343,7 @@ HierarchyBuilder::make_cell_variant (const HierarchyBuilder::CellMapKey &key, co } HierarchyBuilder::new_inst_mode -HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) +HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) { if (all) { @@ -349,6 +354,7 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn if (m_cell_stack.back ().first) { db::CellInstArray new_inst (inst, &mp_target->array_repository ()); new_inst.object () = db::CellInst (new_cell); + new_inst.transform (always_apply); new_inst.transform_into (m_trans); for (std::vector::const_iterator c = m_cell_stack.back ().second.begin (); c != m_cell_stack.back ().second.end (); ++c) { (*c)->insert (new_inst); @@ -367,7 +373,7 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn } bool -HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all) +HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all) { if (all) { @@ -386,7 +392,7 @@ HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db: // for a new cell, create this instance if (m_cell_stack.back ().first) { - db::CellInstArray new_inst (db::CellInst (new_cell), trans); + db::CellInstArray new_inst (db::CellInst (new_cell), always_apply * trans); new_inst.transform_into (m_trans); for (std::vector::const_iterator c = m_cell_stack.back ().second.begin (); c != m_cell_stack.back ().second.end (); ++c) { (*c)->insert (new_inst); @@ -399,11 +405,11 @@ HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db: } void -HierarchyBuilder::shape (const RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans & /*trans*/, const db::Box ®ion, const box_tree_type *complex_region) +HierarchyBuilder::shape (const RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans &apply_always, const db::ICplxTrans & /*trans*/, const db::Box ®ion, const box_tree_type *complex_region) { for (std::vector::const_iterator c = m_cell_stack.back ().second.begin (); c != m_cell_stack.back ().second.end (); ++c) { db::Shapes &shapes = (*c)->shapes (m_target_layer); - mp_pipe->push (shape, m_trans, region, complex_region, &shapes); + mp_pipe->push (shape, m_trans * apply_always, region, complex_region, &shapes); } } @@ -528,9 +534,12 @@ ClippingHierarchyBuilderShapeReceiver::insert_clipped (const db::Box &box, const if (complex_region) { for (db::RecursiveShapeReceiver::box_tree_type::overlapping_iterator cr = complex_region->begin_overlapping (bb, db::box_convert ()); ! cr.at_end (); ++cr) { - mp_pipe->push (*cr & bb, trans, world, 0, target); + db::Box bc = *cr & bb; + if (! bc.empty ()) { + mp_pipe->push (bc, trans, world, 0, target); + } } - } else { + } else if (! bb.empty ()) { mp_pipe->push (bb, trans, world, 0, target); } } diff --git a/src/db/db/dbHierarchyBuilder.h b/src/db/db/dbHierarchyBuilder.h index 2bb1eca0a..27601e8ae 100644 --- a/src/db/db/dbHierarchyBuilder.h +++ b/src/db/db/dbHierarchyBuilder.h @@ -273,9 +273,9 @@ public: virtual void end (const RecursiveShapeIterator *iter); virtual void enter_cell (const RecursiveShapeIterator *iter, const db::Cell *cell, const db::Box ®ion, const box_tree_type *complex_region); virtual void leave_cell (const RecursiveShapeIterator *iter, const db::Cell *cell); - virtual new_inst_mode new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box ®ion, const box_tree_type *complex_region, bool all); - virtual bool new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all); - virtual void shape (const RecursiveShapeIterator *iter, const db::Shape &shape, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region); + virtual new_inst_mode new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const ICplxTrans &always_apply, const db::Box ®ion, const box_tree_type *complex_region, bool all); + virtual bool new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all); + virtual void shape (const RecursiveShapeIterator *iter, const db::Shape &shape, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region); /** * @brief Sets the target layer - shapes will be put there diff --git a/src/db/db/dbMatrix.cc b/src/db/db/dbMatrix.cc index edbb39a2d..5c78bb240 100644 --- a/src/db/db/dbMatrix.cc +++ b/src/db/db/dbMatrix.cc @@ -37,14 +37,16 @@ inline double mnorm (double x) return fabs (x) < 1e-14 ? 0.0 : x; } +template std::string -Matrix2d::to_string () const +matrix_2d::to_string () const { return tl::sprintf ("(%.12g,%.12g) (%.12g,%.12g)", mnorm (m_m11), mnorm (m_m12), mnorm (m_m21), mnorm (m_m22)); } -std::pair -Matrix2d::mag () const +template +std::pair +matrix_2d::mag () const { double s1 = sqrt (m_m11 * m_m11 + m_m21 * m_m21); double s2 = sqrt (m_m12 * m_m12 + m_m22 * m_m22); @@ -52,14 +54,16 @@ Matrix2d::mag () const return std::make_pair (n * s1, n * s2); } +template bool -Matrix2d::has_rotation () const +matrix_2d::has_rotation () const { return fabs (m_m11 - 1.0) > 1e-10 || fabs (m_m12) > 1e-10 || fabs (m_m21) > 1e-10 || fabs (m_m22 - 1.0) > 1e-10; } -double -Matrix2d::angle () const +template +double +matrix_2d::angle () const { std::pair m = mag (); double u1 = m.first; @@ -82,15 +86,17 @@ Matrix2d::angle () const return 180.0 * atan2 (sin_a, cos_a) / M_PI; } -Matrix2d -Matrix2d::rotation (double a) +template +matrix_2d +matrix_2d::rotation (double a) { a *= M_PI / 180.0; return Matrix2d (cos (a), -sin (a), sin (a), cos (a)); } -bool -Matrix2d::has_shear () const +template +bool +matrix_2d::has_shear () const { std::pair m = mag (); double u1 = m.first; @@ -104,8 +110,9 @@ Matrix2d::has_shear () const return fabs (fsin_a) > 1e-10; } -double -Matrix2d::shear_angle () const +template +double +matrix_2d::shear_angle () const { std::pair m = mag (); double u1 = m.first; @@ -124,8 +131,9 @@ Matrix2d::shear_angle () const return 180.0 * atan2 (fsin_a, fcos_a) / M_PI; } -Matrix2d -Matrix2d::shear (double a) +template +matrix_2d +matrix_2d::shear (double a) { a *= M_PI / 180.0; double cos_a = cos (a); @@ -134,21 +142,32 @@ Matrix2d::shear (double a) return Matrix2d (f * cos_a, f * sin_a, f * sin_a, f * cos_a); } -bool -Matrix2d::is_ortho () const +template +bool +matrix_2d::is_ortho () const { return fabs (m_m11 * m_m12 + m_m21 * m_m22) < 1e-10 && fabs (m_m11 * m_m12) < 1e-10 && fabs (m_m21 * m_m22) < 1e-10; } -bool -Matrix2d::equal (const Matrix2d &d) const +template +bool +matrix_2d::is_unity () const +{ + static matrix_2d u; + return equal (u); +} + +template +bool +matrix_2d::equal (const matrix_2d &d) const { return fabs (m_m11 - d.m_m11) < 1e-10 && fabs (m_m12 - d.m_m12) < 1e-10 && fabs (m_m21 - d.m_m21) < 1e-10 && fabs (m_m22 - d.m_m22) < 1e-10; } -bool -Matrix2d::less (const Matrix2d &d) const +template +bool +matrix_2d::less (const matrix_2d &d) const { if (fabs (m_m11 - d.m_m11) > 1e-10) { return m_m11 < d.m_m11; @@ -165,10 +184,14 @@ Matrix2d::less (const Matrix2d &d) const return false; } +template class matrix_2d; +template class matrix_2d; + // -------------------------------------------------------------------------------------------- -double -Matrix3d::det () const +template +double +matrix_3d::det () const { double d = 0.0; for (int i0 = 0; i0 < 3; ++i0) { @@ -182,8 +205,9 @@ Matrix3d::det () const return d; } -db::DVector -Matrix3d::trans (const db::DPoint &p, const db::DVector &v) const +template +db::vector +matrix_3d::trans (const db::point &p, const db::vector &v) const { double t[2][2]; for (int i = 0; i < 2; ++i) { @@ -191,11 +215,12 @@ Matrix3d::trans (const db::DPoint &p, const db::DVector &v) const t[i][j] = (m_m[i][j] * m_m[2][1 - j] - m_m[i][1 - j] * m_m[2][j]) * (j == 0 ? p.y() : p.x()) + (m_m[2][2] * m_m[i][j] - m_m[i][2] * m_m[2][j]); } } - return db::DVector(v.x() * t[0][0] + v.y() * t[0][1], v.x() * t[1][0] + v.y() * t[1][1]); + return db::vector(v.x() * t[0][0] + v.y() * t[0][1], v.x() * t[1][0] + v.y() * t[1][1]); } +template bool -Matrix3d::can_transform (const db::DPoint &p) const +matrix_3d::can_transform (const db::point &p) const { double r[3] = { 0, 0, 0 }; for (int i = 0; i < 3; ++i) { @@ -205,8 +230,9 @@ Matrix3d::can_transform (const db::DPoint &p) const return (r[2] > (std::abs (r[0]) + std::abs (r[1])) * 1e-10); } -db::DPoint -Matrix3d::trans (const db::DPoint &p) const +template +db::point +matrix_3d::trans (const db::point &p) const { double r[3] = { 0, 0, 0 }; for (int i = 0; i < 3; ++i) { @@ -215,14 +241,15 @@ Matrix3d::trans (const db::DPoint &p) const // safe approximation to the forbidden area where z <= 0 double z = std::max (r [2], (std::abs (r[0]) + std::abs (r[1])) * 1e-10); - return db::DPoint (r[0] / z, r[1] / z); + return db::point (r[0] / z, r[1] / z); } -Matrix3d -Matrix3d::inverted () const +template +matrix_3d +matrix_3d::inverted () const { double m[3][3]; - Matrix3d r (1.0); + matrix_3d r (1.0); for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { @@ -268,71 +295,88 @@ Matrix3d::inverted () const return r; } -db::DVector -Matrix3d::disp () const +template +db::vector +matrix_3d::disp () const { - return db::DVector (m_m[0][2] / m_m[2][2], m_m[1][2] / m_m[2][2]); + return db::vector (m_m[0][2] / m_m[2][2], m_m[1][2] / m_m[2][2]); } -double -Matrix3d::perspective_tilt_x (double z) const +template +double +matrix_3d::perspective_tilt_x (double z) const { - db::DVector d = disp (); - db::Matrix3d m = db::Matrix3d::disp (-d) * *this; + db::vector d = disp (); + db::matrix_3d m = db::matrix_3d::disp (-d) * *this; return 180 * atan (z * (m.m ()[2][0] * m.m ()[1][1] - m.m ()[2][1] * m.m ()[1][0]) / (m.m ()[0][0] * m.m ()[1][1] - m.m ()[0][1] * m.m ()[1][0])) / M_PI; } -double -Matrix3d::perspective_tilt_y (double z) const +template +double +matrix_3d::perspective_tilt_y (double z) const { - db::DVector d = disp (); - db::Matrix3d m = db::Matrix3d::disp (-d) * *this; + db::vector d = disp (); + db::matrix_3d m = db::matrix_3d::disp (-d) * *this; return 180 * atan (z * (m.m ()[2][1] * m.m ()[0][0] - m.m ()[2][0] * m.m ()[0][1]) / (m.m ()[0][0] * m.m ()[1][1] - m.m ()[0][1] * m.m ()[1][0])) / M_PI; } -bool -Matrix3d::has_perspective () const +template +bool +matrix_3d::has_perspective () const { return fabs (m_m[2][0]) + fabs (m_m[2][1]) > 1e-10; } -Matrix3d -Matrix3d::perspective (double tx, double ty, double z) +template +matrix_3d +matrix_3d::perspective (double tx, double ty, double z) { tx *= M_PI / 180.0; ty *= M_PI / 180.0; - return Matrix3d (1.0, 0.0, 0.0, 1.0, 0.0, 0.0, tan (tx) / z, tan (ty) / z); + return matrix_3d (1.0, 0.0, 0.0, 1.0, 0.0, 0.0, tan (tx) / z, tan (ty) / z); } -Matrix2d -Matrix3d::m2d () const +template +matrix_2d +matrix_3d::m2d () const { - db::DVector d = disp (); - db::Matrix3d m = db::Matrix3d::disp (-d) * *this; + db::vector d = disp (); + db::matrix_3d m = db::matrix_3d::disp (-d) * *this; if (has_perspective ()) { - m = Matrix3d::perspective (-perspective_tilt_x (1.0), -perspective_tilt_y (1.0), 1.0) * m; + m = matrix_3d::perspective (-perspective_tilt_x (1.0), -perspective_tilt_y (1.0), 1.0) * m; } - return Matrix2d (m.m_m[0][0] / m.m_m[2][2], m.m_m[0][1] / m.m_m[2][2], m.m_m[1][0] / m.m_m[2][2], m.m_m[1][1] / m.m_m[2][2]); + return matrix_2d (m.m_m[0][0] / m.m_m[2][2], m.m_m[0][1] / m.m_m[2][2], m.m_m[1][0] / m.m_m[2][2], m.m_m[1][1] / m.m_m[2][2]); } -std::string -Matrix3d::to_string () const +template +std::string +matrix_3d::to_string () const { return tl::sprintf ("(%.12g,%.12g,%.12g)", mnorm (m_m[0][0]), mnorm (m_m[0][1]), mnorm (m_m[0][2])) + " " + tl::sprintf ("(%.12g,%.12g,%.12g)", mnorm (m_m[1][0]), mnorm (m_m[1][1]), mnorm (m_m[1][2])) + " " + tl::sprintf ("(%.12g,%.12g,%.12g)", mnorm (m_m[2][0]), mnorm (m_m[2][1]), mnorm (m_m[2][2])); } -bool -Matrix3d::is_ortho () const +template +bool +matrix_3d::is_ortho () const { return ! has_perspective () && m2d ().is_ortho (); } -bool -Matrix3d::equal (const Matrix3d &d) const +template +bool +matrix_3d::is_unity () const +{ + static matrix_3d u; + return equal (u); +} + +template +bool +matrix_3d::equal (const matrix_3d &d) const { for (unsigned int i = 0; i < 3; ++i) { for (unsigned int j = 0; j < 3; ++j) { @@ -344,8 +388,9 @@ Matrix3d::equal (const Matrix3d &d) const return true; } -bool -Matrix3d::less (const Matrix3d &d) const +template +bool +matrix_3d::less (const matrix_3d &d) const { for (unsigned int i = 0; i < 3; ++i) { for (unsigned int j = 0; j < 3; ++j) { @@ -357,6 +402,9 @@ Matrix3d::less (const Matrix3d &d) const return false; } +template class matrix_3d; +template class matrix_3d; + // -------------------------------------------------------------------------------------------- /** @@ -775,7 +823,7 @@ adjust_matrix (Matrix3d &matrix, const std::vector &landmarks_befor namespace tl { - template<> bool test_extractor_impl (tl::Extractor &ex, db::Matrix2d &m) + template bool test_extractor_impl_matrix2d (tl::Extractor &ex, db::matrix_2d &m) { double m11 = 0.0, m12 = 0.0, m21 = 0.0, m22 = 0.0; @@ -811,18 +859,18 @@ namespace tl return false; } - m = db::Matrix2d (m11, m12, m21, m22); + m = db::matrix_2d (m11, m12, m21, m22); return true; } - template<> void extractor_impl (tl::Extractor &ex, db::Matrix2d &m) + template void extractor_impl_matrix2d (tl::Extractor &ex, db::matrix_2d &m) { if (! test_extractor_impl (ex, m)) { ex.error (tl::to_string (tr ("Expected a 2d matrix specification"))); } } - template<> bool test_extractor_impl (tl::Extractor &ex, db::Matrix3d &m) + template bool test_extractor_impl_matrix3d (tl::Extractor &ex, db::matrix_3d &m) { double m11 = 0.0, m12 = 0.0, m13 = 0.0, m21 = 0.0, m22 = 0.0, m23 = 0.0, m31 = 0.0, m32 = 0.0, m33 = 0.0; @@ -892,16 +940,56 @@ namespace tl return false; } - m = db::Matrix3d (m11, m12, m13, m21, m22, m23, m31, m32, m33); + m = db::matrix_3d (m11, m12, m13, m21, m22, m23, m31, m32, m33); return true; } - template<> void extractor_impl (tl::Extractor &ex, db::Matrix3d &m) + template void extractor_impl_matrix3d (tl::Extractor &ex, db::matrix_3d &m) { if (! test_extractor_impl (ex, m)) { ex.error (tl::to_string (tr ("Expected a 3d matrix specification"))); } } + + template<> void extractor_impl > (tl::Extractor &ex, db::matrix_2d &m) + { + extractor_impl_matrix2d (ex, m); + } + + template<> void extractor_impl > (tl::Extractor &ex, db::matrix_2d &m) + { + extractor_impl_matrix2d (ex, m); + } + + template<> void extractor_impl > (tl::Extractor &ex, db::matrix_3d &m) + { + extractor_impl_matrix3d (ex, m); + } + + template<> void extractor_impl > (tl::Extractor &ex, db::matrix_3d &m) + { + extractor_impl_matrix3d (ex, m); + } + + template<> bool test_extractor_impl > (tl::Extractor &ex, db::matrix_2d &m) + { + return test_extractor_impl_matrix2d (ex, m); + } + + template<> bool test_extractor_impl > (tl::Extractor &ex, db::matrix_2d &m) + { + return test_extractor_impl_matrix2d (ex, m); + } + + template<> bool test_extractor_impl > (tl::Extractor &ex, db::matrix_3d &m) + { + return test_extractor_impl_matrix3d (ex, m); + } + + template<> bool test_extractor_impl > (tl::Extractor &ex, db::matrix_3d &m) + { + return test_extractor_impl_matrix3d (ex, m); + } } diff --git a/src/db/db/dbMatrix.h b/src/db/db/dbMatrix.h index 9f28ce9d9..8e5ab74c2 100644 --- a/src/db/db/dbMatrix.h +++ b/src/db/db/dbMatrix.h @@ -42,23 +42,24 @@ namespace db /** * @brief A class representing a 2d matrix, mainly to represent a rotation or shear transformation of 2d vectors */ -class DB_PUBLIC Matrix2d +template +class DB_PUBLIC matrix_2d { public: /** * @brief typedefs for compatibility with the other transformations */ - typedef double target_coord_type; - typedef double coord_type; - typedef db::DPoint displacement_type; - typedef Matrix2d inverse_trans; + typedef C target_coord_type; + typedef C coord_type; + typedef db::point displacement_type; + typedef matrix_2d inverse_trans; /** * @brief Default ctor * * Creates a null matrix */ - Matrix2d () + matrix_2d () : m_m11 (0.0), m_m12 (0.0), m_m21 (0.0), m_m22 (0.0) { // .. nothing yet .. @@ -69,18 +70,30 @@ public: * * Creates a matrix (m11, m12) (m21, m22) */ - Matrix2d (double m11, double m12, double m21, double m22) + matrix_2d (double m11, double m12, double m21, double m22) : m_m11 (m11), m_m12 (m12), m_m21 (m21), m_m22 (m22) { // .. nothing yet .. } + /** + * @brief Full ctor + * + * Creates a matrix (m11, m12) (m21, m22) + */ + template + matrix_2d (const matrix_2d &m) + : m_m11 (m.m11 ()), m_m12 (m.m12 ()), m_m21 (m.m21 ()), m_m22 (m.m22 ()) + { + // .. nothing yet .. + } + /** * @brief Scalar ctor * * Creates a matrix (d, 0) (0, d) */ - Matrix2d (double d) + matrix_2d (double d) : m_m11 (d), m_m12 (0.0), m_m21 (0.0), m_m22 (d) { // .. nothing yet .. @@ -91,7 +104,7 @@ public: * * Creates a matrix (d1, 0) (0, d2) */ - Matrix2d (double d1, double d2) + matrix_2d (double d1, double d2) : m_m11 (d1), m_m12 (0.0), m_m21 (0.0), m_m22 (d2) { // .. nothing yet .. @@ -101,7 +114,7 @@ public: * @brief Make a matrix from a transformation */ template - Matrix2d (const Tr &t) + matrix_2d (const Tr &t) { *this = t.to_matrix2d (); } @@ -109,9 +122,10 @@ public: /** * @brief Add operator */ - Matrix2d operator+ (const Matrix2d &other) const + template + matrix_2d operator+ (const matrix_2d &other) const { - Matrix2d m (*this); + matrix_2d m (*this); m += other; return m; } @@ -119,7 +133,8 @@ public: /** * @brief Add to operator */ - Matrix2d &operator+= (const Matrix2d &other) + template + matrix_2d &operator+= (const matrix_2d &other) { m_m11 += other.m_m11; m_m12 += other.m_m12; @@ -131,18 +146,19 @@ public: /** * @brief Product of two matrices */ - Matrix2d operator* (const Matrix2d &other) const + template + matrix_2d operator* (const matrix_2d &other) const { - return Matrix2d (m_m11 * other.m_m11 + m_m12 * other.m_m21, - m_m11 * other.m_m12 + m_m12 * other.m_m22, - m_m21 * other.m_m11 + m_m22 * other.m_m21, - m_m21 * other.m_m12 + m_m22 * other.m_m22); + return matrix_2d (m_m11 * other.m_m11 + m_m12 * other.m_m21, + m_m11 * other.m_m12 + m_m12 * other.m_m22, + m_m21 * other.m_m11 + m_m22 * other.m_m21, + m_m21 * other.m_m12 + m_m22 * other.m_m22); } /** * @brief Multiply another to this matrix */ - Matrix2d &operator*= (const Matrix2d &other) + matrix_2d &operator*= (const matrix_2d &other) { *this = (*this * other); return *this; @@ -151,9 +167,9 @@ public: /** * @brief Multiply with a scalar */ - Matrix2d operator* (double d) const + matrix_2d operator* (double d) const { - Matrix2d m (*this); + matrix_2d m (*this); m *= d; return m; } @@ -161,7 +177,7 @@ public: /** * @brief Multiply a scalar to this matrix */ - Matrix2d &operator*= (double d) + matrix_2d &operator*= (double d) { m_m11 *= d; m_m12 *= d; @@ -173,15 +189,15 @@ public: /** * @brief Transformation of a vector */ - db::DVector operator* (const db::DVector &v) const + db::vector operator* (const db::vector &v) const { - return db::DVector (m_m11 * v.x () + m_m12 * v.y (), m_m21 * v.x () + m_m22 * v.y ()); + return db::vector (m_m11 * v.x () + m_m12 * v.y (), m_m21 * v.x () + m_m22 * v.y ()); } /** * @brief "trans" alias for compatibility with the other transformations */ - db::DVector trans (const db::DVector &p) const + db::vector trans (const db::vector &p) const { return operator* (p); } @@ -189,7 +205,7 @@ public: /** * @brief "operator()" alias for compatibility with the other transformations */ - db::DVector operator() (const db::DVector &p) const + db::vector operator() (const db::vector &p) const { return operator* (p); } @@ -197,15 +213,15 @@ public: /** * @brief Transformation of a point */ - db::DPoint operator* (const db::DPoint &v) const + db::point operator* (const db::point &v) const { - return db::DPoint (m_m11 * v.x () + m_m12 * v.y (), m_m21 * v.x () + m_m22 * v.y ()); + return db::point (m_m11 * v.x () + m_m12 * v.y (), m_m21 * v.x () + m_m22 * v.y ()); } /** * @brief "trans" alias for compatibility with the other transformations */ - db::DPoint trans (const db::DPoint &p) const + db::point trans (const db::point &p) const { return operator* (p); } @@ -213,7 +229,7 @@ public: /** * @brief "operator()" alias for compatibility with the other transformations */ - db::DPoint operator() (const db::DPoint &p) const + db::point operator() (const db::point &p) const { return operator* (p); } @@ -221,9 +237,9 @@ public: /** * @brief Return the transposed matrix */ - Matrix2d transposed () const + matrix_2d transposed () const { - return Matrix2d (m_m11, m_m21, m_m12, m_m22); + return matrix_2d (m_m11, m_m21, m_m12, m_m22); } /** @@ -245,9 +261,9 @@ public: /** * @brief Return the inverted matrix */ - Matrix2d inverted () const + matrix_2d inverted () const { - Matrix2d m (*this); + matrix_2d m (*this); m.invert (); return m; } @@ -325,9 +341,9 @@ public: * @param mx The x magnification * @param my The y magnification */ - static Matrix2d mag (double mx, double my) + static matrix_2d mag (double mx, double my) { - return Matrix2d (mx, 0.0, 0.0, my); + return matrix_2d (mx, 0.0, 0.0, my); } /** @@ -335,9 +351,9 @@ public: * * @param m The magnification */ - static Matrix2d mag (double m) + static matrix_2d mag (double m) { - return Matrix2d (m, 0.0, 0.0, m); + return matrix_2d (m, 0.0, 0.0, m); } /** @@ -356,9 +372,9 @@ public: /** * @brief Create the mirror matrix */ - static Matrix2d mirror (bool m) + static matrix_2d mirror (bool m) { - return Matrix2d (1.0, 0.0, 0.0, m ? -1.0 : 1.0); + return matrix_2d (1.0, 0.0, 0.0, m ? -1.0 : 1.0); } /** @@ -380,7 +396,7 @@ public: /** * @brief Create the rotation matrix from the given angle */ - static Matrix2d rotation (double a); + static matrix_2d rotation (double a); /** * @brief Determine the shear component of the matrix and return the shear angle in degree @@ -403,7 +419,7 @@ public: /** * @brief Create the shear matrix from the given angle */ - static Matrix2d shear (double a); + static matrix_2d shear (double a); /** * @brief Determine whether the matrix represents an orthogonal transformation @@ -412,6 +428,13 @@ public: */ bool is_ortho () const; + /** + * @brief Determine whether the matrix represents an unit transformation + * + * This method is provided for compatibility to the other transformations. + */ + bool is_unity () const; + /** * @brief Convert to a string */ @@ -420,37 +443,41 @@ public: /** * @brief A fuzzy compare operator (equal) */ - bool equal (const Matrix2d &d) const; + bool equal (const matrix_2d &d) const; /** * @brief A fuzzy compare operator (less) */ - bool less (const Matrix2d &d) const; + bool less (const matrix_2d &d) const; private: double m_m11, m_m12, m_m21, m_m22; }; +typedef matrix_2d Matrix2d; +typedef matrix_2d IMatrix2d; + /** * @brief A class representing a 3d matrix, mainly to represent a rotation, shear or perspective transformation of 2d vectors */ -class DB_PUBLIC Matrix3d +template +class DB_PUBLIC matrix_3d { public: /** * @brief typedefs for compatibility with the other transformations */ - typedef double target_coord_type; - typedef double coord_type; - typedef db::DPoint displacement_type; - typedef Matrix3d inverse_trans; + typedef C target_coord_type; + typedef C coord_type; + typedef db::point displacement_type; + typedef matrix_3d inverse_trans; /** * @brief Default ctor * * Creates a null matrix */ - Matrix3d () + matrix_3d () { set (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); } @@ -460,7 +487,7 @@ public: * * Creates a matrix (m11, m12, 0) (m21, m22, 0) (0, 0, 1) */ - Matrix3d (double m11, double m12, double m21, double m22) + matrix_3d (double m11, double m12, double m21, double m22) { set (m11, m12, 0.0, m21, m22, 0.0, 0.0, 0.0, 1.0); } @@ -470,17 +497,33 @@ public: * * Creates a matrix (m11, m12, m13) (m21, m22, m23) (m31, m32, m33) */ - Matrix3d (double m11, double m12, double m13, double m21, double m22, double m23, double m31, double m32, double m33) + matrix_3d (double m11, double m12, double m13, double m21, double m22, double m23, double m31, double m32, double m33) { set (m11, m12, m13, m21, m22, m23, m31, m32, m33); } + /** + * @brief Full ctor + * + * Creates a matrix (m11, m12) (m21, m22) + */ + template + matrix_3d (const matrix_3d &m) + { + const double (&mm) [3][3] = m.m (); + for (unsigned int i = 0; i < 3; ++i) { + for (unsigned int j = 0; j < 3; ++j) { + m_m [i][j] = mm [i][j]; + } + } + } + /** * @brief Matrix2d ctor from eight components * * Creates a matrix (m11, m12, d1) (m21, m22, d2) (p1, p2, 1) */ - Matrix3d (double m11, double m12, double m21, double m22, double d1, double d2, double p1, double p2) + matrix_3d (double m11, double m12, double m21, double m22, double d1, double d2, double p1, double p2) { set (m11, m12, d1, m21, m22, d2, p1, p2, 1.0); } @@ -490,7 +533,8 @@ public: * * Creates a matrix representing the given Matrix2d. */ - explicit Matrix3d (const Matrix2d &m) + template + explicit matrix_3d (const matrix_2d &m) { set (m.m11 (), m.m12 (), 0.0, m.m21 (), m.m22 (), 0.0, 0.0, 0.0, 1.0); } @@ -499,7 +543,7 @@ public: * @brief Make a matrix from a transformation */ template - explicit Matrix3d (const Tr &t) + explicit matrix_3d (const Tr &t) { *this = t.to_matrix3d (); } @@ -509,7 +553,7 @@ public: * * Creates a matrix (d, 0, 0) (0, d, 0) (0, 0, 1) */ - explicit Matrix3d (double d) + explicit matrix_3d (double d) { set (d, 0.0, 0.0, 0.0, d, 0.0, 0.0, 0.0, 1.0); } @@ -517,9 +561,10 @@ public: /** * @brief Add operator */ - Matrix3d operator+ (const Matrix3d &other) const + template + matrix_3d operator+ (const matrix_3d &other) const { - Matrix3d m (*this); + matrix_3d m (*this); m += other; return m; } @@ -527,7 +572,8 @@ public: /** * @brief Add to operator */ - Matrix3d &operator+= (const Matrix3d &other) + template + matrix_3d &operator+= (const matrix_3d &other) { for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { @@ -540,13 +586,14 @@ public: /** * @brief Product of two matrices */ - Matrix3d operator* (const Matrix3d &other) const + template + matrix_3d operator* (const matrix_3d &other) const { - Matrix3d res; + matrix_3d res; for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { for (int k = 0; k < 3; ++k) { - res.m_m [i][j] += m_m [i][k] * other.m_m [k][j]; + res.m_m [i][j] += m_m [i][k] * other.m () [k][j]; } } } @@ -556,7 +603,8 @@ public: /** * @brief Multiply another to this matrix */ - Matrix3d &operator*= (const Matrix3d &other) + template + matrix_3d &operator*= (const matrix_3d &other) { *this = (*this * other); return *this; @@ -565,9 +613,9 @@ public: /** * @brief Multiply with a scalar */ - Matrix3d operator* (double d) const + matrix_3d operator* (double d) const { - Matrix3d m (*this); + matrix_3d m (*this); m *= d; return m; } @@ -575,7 +623,7 @@ public: /** * @brief Multiply a scalar to this matrix */ - Matrix3d &operator*= (double d) + matrix_3d &operator*= (double d) { for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { @@ -591,17 +639,17 @@ public: * A point can be transformed if the resulting point is * located in the positive z plane. */ - bool can_transform (const db::DPoint &p) const; + bool can_transform (const db::point &p) const; /** * @brief Transforms a vector which emerges from a certain point */ - db::DVector trans (const db::DPoint &p, const db::DVector &v) const; + db::vector trans (const db::point &p, const db::vector &v) const; /** * @brief Transforms a point */ - db::DPoint trans (const db::DPoint &p) const; + db::point trans (const db::point &p) const; /** * @brief Transforms a vector @@ -613,34 +661,34 @@ public: * In this implementation we assume the vector starts at 0, 0. This at least renders this * feature useful for implementing shear and anisotropic scaling. */ - db::DVector trans (const db::DVector &p) const + db::vector trans (const db::vector &p) const { - return this->trans (db::DPoint () + p) - this->trans (db::DPoint ()); + return this->trans (db::point () + p) - this->trans (db::point ()); } /** * @brief "trans" alias for compatibility with the other transformations */ - template - db::DPoint trans (const db::point &p) const + template + db::point trans (const db::point &p) const { - return trans (db::DPoint (p)); + return trans (db::point (p)); } /** * @brief "trans" alias for compatibility with the other transformations */ - template - db::DVector trans (const db::vector &p) const + template + db::vector trans (const db::vector &p) const { - return trans (db::DVector (p)); + return trans (db::vector (p)); } /** * @brief "operator()" alias for compatibility with the other transformations */ - template - db::DPoint operator() (const db::point &p) const + template + db::point operator() (const db::point &p) const { return trans (p); } @@ -648,8 +696,8 @@ public: /** * @brief "operator()" alias for compatibility with the other transformations */ - template - db::DVector operator() (const db::vector &p) const + template + db::vector operator() (const db::vector &p) const { return trans (p); } @@ -657,9 +705,9 @@ public: /** * @brief Return the transposed matrix */ - Matrix3d transposed () const + matrix_3d transposed () const { - Matrix3d res; + matrix_3d res; for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { res.m_m [i][j] = m_m [j][i]; @@ -684,7 +732,7 @@ public: /** * @brief Return the inverted matrix */ - Matrix3d inverted () const; + matrix_3d inverted () const; /** * @brief In-place invert @@ -697,7 +745,12 @@ public: /** * @brief Accessor to the internal matrix */ - const double (*m () const) [3] { return m_m; } + const double (&m () const) [3][3] { return m_m; } + + /** + * @brief Accessor to the internal matrix + */ + double (&m ()) [3][3] { return m_m; } /** * @brief Return the magnification component of the matrix @@ -730,17 +783,17 @@ public: /** * @brief Create the magnification matrix with isotropic magnification */ - static Matrix3d mag (double m) + static matrix_3d mag (double m) { - return Matrix3d (m, 0.0, 0.0, m); + return matrix_3d (m, 0.0, 0.0, m); } /** * @brief Create the magnification matrix with anisotropic magnification */ - static Matrix3d mag (double mx, double my) + static matrix_3d mag (double mx, double my) { - return Matrix3d (mx, 0.0, 0.0, my); + return matrix_3d (mx, 0.0, 0.0, my); } /** @@ -759,9 +812,9 @@ public: /** * @brief Create the mirror matrix */ - static Matrix3d mirror (bool m) + static matrix_3d mirror (bool m) { - return Matrix3d (1.0, 0.0, 0.0, m ? -1.0 : 1.0); + return matrix_3d (1.0, 0.0, 0.0, m ? -1.0 : 1.0); } /** @@ -789,9 +842,9 @@ public: /** * @brief Create the rotation matrix from the given angle */ - static Matrix3d rotation (double a) + static matrix_3d rotation (double a) { - return Matrix3d (Matrix2d::rotation (a)); + return matrix_3d (Matrix2d::rotation (a)); } /** @@ -821,9 +874,9 @@ public: /** * @brief Create the shear matrix from the given angle */ - static Matrix3d shear (double a) + static matrix_3d shear (double a) { - return Matrix3d (Matrix2d::shear (a)); + return matrix_3d (Matrix2d::shear (a)); } /** @@ -860,19 +913,19 @@ public: * @param ty The tilt angle in y direction (around the x axis) in degree for the given observer distance. * @param z The observer distance. */ - static Matrix3d perspective (double tx, double ty, double z); + static matrix_3d perspective (double tx, double ty, double z); /** * @brief Get the displacement vector component */ - db::DVector disp () const; + db::vector disp () const; /** * @brief Create the mirror matrix */ - static Matrix3d disp (const db::DVector &d) + static matrix_3d disp (const db::vector &d) { - return Matrix3d (1.0, 0.0, 0.0, 1.0, d.x (), d.y (), 0.0, 0.0); + return matrix_3d (1.0, 0.0, 0.0, 1.0, d.x (), d.y (), 0.0, 0.0); } /** @@ -882,10 +935,17 @@ public: */ bool is_ortho () const; + /** + * @brief Determine whether the matrix represents an unit transformation + * + * This method is provided for compatibility to the other transformations. + */ + bool is_unity () const; + /** * @brief Get the 2d matrix component (without perspective transformation or displacement) */ - Matrix2d m2d () const; + matrix_2d m2d () const; /** * @brief Convert to a string @@ -895,12 +955,12 @@ public: /** * @brief A fuzzy compare operator (equal) */ - bool equal (const Matrix3d &d) const; + bool equal (const matrix_3d &d) const; /** * @brief A fuzzy compare operator (less) */ - bool less (const Matrix3d &d) const; + bool less (const matrix_3d &d) const; private: double m_m[3][3]; @@ -919,6 +979,9 @@ private: } }; +typedef matrix_3d Matrix3d; +typedef matrix_3d IMatrix3d; + /** * @brief Some adjustment flags * @@ -982,10 +1045,15 @@ void DB_PUBLIC adjust_matrix (Matrix3d &matrix, const std::vector & namespace tl { - template<> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::Matrix2d &t); - template<> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::Matrix3d &t); - template<> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::Matrix2d &t); - template<> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::Matrix3d &t); + template<> DB_PUBLIC void extractor_impl > (tl::Extractor &ex, db::matrix_2d &t); + template<> DB_PUBLIC void extractor_impl > (tl::Extractor &ex, db::matrix_2d &t); + template<> DB_PUBLIC void extractor_impl > (tl::Extractor &ex, db::matrix_3d &t); + template<> DB_PUBLIC void extractor_impl > (tl::Extractor &ex, db::matrix_3d &t); + + template<> DB_PUBLIC bool test_extractor_impl > (tl::Extractor &ex, db::matrix_2d &t); + template<> DB_PUBLIC bool test_extractor_impl > (tl::Extractor &ex, db::matrix_2d &t); + template<> DB_PUBLIC bool test_extractor_impl > (tl::Extractor &ex, db::matrix_3d &t); + template<> DB_PUBLIC bool test_extractor_impl > (tl::Extractor &ex, db::matrix_3d &t); } // namespace tl #endif diff --git a/src/db/db/dbPolygonTools.cc b/src/db/db/dbPolygonTools.cc index 0aa4839d1..808133e68 100644 --- a/src/db/db/dbPolygonTools.cc +++ b/src/db/db/dbPolygonTools.cc @@ -1565,8 +1565,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 (); @@ -1582,18 +1609,30 @@ 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_nx = nx; - m_ny = ny; + m_p = db::Vector (std::min (d.x (), p.x ()), std::min (d.y (), p.y ())); + + if (nx != m_nx || ny != m_ny) { + + m_nx = nx; + m_ny = ny; + + if (mp_av) { + delete mp_av; + } + + mp_av = new area_type [nx * ny]; - if (mp_av) { - delete mp_av; } - mp_av = new area_type [nx * ny]; - clear (); } @@ -1613,6 +1652,7 @@ AreaMap::swap (AreaMap &other) { std::swap (m_p0, other.m_p0); std::swap (m_d, other.m_d); + std::swap (m_p, other.m_p); std::swap (m_nx, other.m_nx); std::swap (m_ny, other.m_ny); std::swap (mp_av, other.mp_av); @@ -1631,10 +1671,20 @@ 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 -void +bool rasterize (const db::Polygon &polygon, db::AreaMap &am) { typedef db::AreaMap::area_type area_type; @@ -1643,11 +1693,12 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am) // check if the polygon overlaps the rasterization area. Otherwise, we simply do nothing. if (! pbox.overlaps (box)) { - return; + return false; } 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 (); @@ -1659,7 +1710,7 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am) // no scanning required (i.e. degenerated polygon) -> do nothing if (iy0 == iy1 || ix0 == ix1) { - return; + return false; } // collect edges @@ -1689,11 +1740,15 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am) ++c; } + if (c == edges.end ()) { + return false; + } + std::vector ::iterator f = c; 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; } @@ -1706,10 +1761,10 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am) std::vector ::iterator cc = c; - while (cc != edges.end () && db::edge_xmax (*cc) <= x) { + 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; } @@ -1717,7 +1772,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 @@ -1728,6 +1784,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 @@ -1737,24 +1801,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); + } } @@ -1762,8 +1852,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) { @@ -1774,6 +1866,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) { @@ -1785,6 +1884,7 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am) } + return true; } // ------------------------------------------------------------------------- diff --git a/src/db/db/dbPolygonTools.h b/src/db/db/dbPolygonTools.h index d59188163..cd3b873cb 100644 --- a/src/db/db/dbPolygonTools.h +++ b/src/db/db/dbPolygonTools.h @@ -503,21 +503,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 */ @@ -579,13 +599,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 @@ -597,7 +622,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 ()); } /** @@ -609,11 +634,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 &); }; /** @@ -621,8 +643,10 @@ private: * * This will decompose the polygon and produce per-pixel area values for the given * polygon. The area contributions will be added to the given area map. + * + * Returns a value indicating whether the map will be non-empty. */ -void DB_PUBLIC rasterize (const db::Polygon &polygon, db::AreaMap &am); +bool DB_PUBLIC rasterize (const db::Polygon &polygon, db::AreaMap &am); /** * @brief Minkowsky sum of an edge and a polygon diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index a72fc5e1e..075752113 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -72,6 +72,7 @@ RecursiveShapeIterator &RecursiveShapeIterator::operator= (const RecursiveShapeI m_current_layer = d.m_current_layer; m_shape = d.m_shape; m_trans = d.m_trans; + m_global_trans = d.m_global_trans; m_trans_stack = d.m_trans_stack; m_inst_iterators = d.m_inst_iterators; m_inst_array_iterators = d.m_inst_array_iterators; @@ -286,6 +287,7 @@ RecursiveShapeIterator::init () m_shape_quad_id = 0; mp_cell = 0; m_current_layer = 0; + m_global_trans = cplx_trans_type (); } void @@ -318,6 +320,26 @@ RecursiveShapeIterator::init_region (const RecursiveShapeIterator::region_type & } } +void +RecursiveShapeIterator::set_global_trans (const cplx_trans_type &tr) +{ + if (m_global_trans != tr) { + m_global_trans = tr; + m_needs_reinit = true; + } +} + +const db::RecursiveShapeIterator::cplx_trans_type & +RecursiveShapeIterator::always_apply () const +{ + if (m_trans_stack.empty ()) { + return m_global_trans; + } else { + static cplx_trans_type unity; + return unity; + } +} + void RecursiveShapeIterator::set_region (const box_type ®ion) { @@ -420,13 +442,13 @@ RecursiveShapeIterator::validate (RecursiveShapeReceiver *receiver) const m_inst_quad_id_stack.clear (); m_inst_array_iterators.clear (); m_cells.clear (); - m_trans = cplx_trans_type (); + m_trans = m_global_trans; m_current_layer = 0; m_shape = shape_iterator (); m_shape_quad_id = 0; m_local_region_stack.clear (); - m_local_region_stack.push_back (m_region); + m_local_region_stack.push_back (m_global_trans.inverted () * m_region); m_local_complex_region_stack.clear (); if (mp_complex_region.get ()) { @@ -571,6 +593,8 @@ RecursiveShapeIterator::bbox () const } } + box = box.transformed (m_global_trans); + if (m_region != box_type::world ()) { box &= m_region; } @@ -749,8 +773,8 @@ RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const box_type new_region = box_type::world (); // compute the region inside the new cell - if (new_region != m_local_region_stack.front ()) { - new_region = m_trans.inverted () * m_local_region_stack.front (); + if (new_region != m_region) { + new_region = m_trans.inverted () * m_region; new_region &= cell ()->bbox (); } m_local_region_stack.push_back (new_region); @@ -911,7 +935,7 @@ RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const RecursiveShapeReceiver::new_inst_mode ni = RecursiveShapeReceiver::NI_all; if (receiver) { - ni = receiver->new_inst (this, m_inst->cell_inst (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), all_of_instance); + ni = receiver->new_inst (this, m_inst->cell_inst (), always_apply (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), all_of_instance); } if (ni == RecursiveShapeReceiver::NI_skip) { @@ -956,7 +980,7 @@ RecursiveShapeIterator::new_inst_member (RecursiveShapeReceiver *receiver) const } while (! m_inst_array.at_end () && receiver) { - if (receiver->new_inst_member (this, m_inst->cell_inst (), m_inst->complex_trans (*m_inst_array), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), is_all_of_instance ())) { + if (receiver->new_inst_member (this, m_inst->cell_inst (), always_apply (), m_inst->complex_trans (*m_inst_array), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), is_all_of_instance ())) { break; } else { ++m_inst_array; @@ -999,7 +1023,7 @@ RecursiveShapeIterator::push (RecursiveShapeReceiver *receiver) validate (receiver); while (! at_end ()) { - receiver->shape (this, *m_shape, m_trans, m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back ()); + receiver->shape (this, *m_shape, always_apply (), m_trans, m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back ()); next (receiver); } diff --git a/src/db/db/dbRecursiveShapeIterator.h b/src/db/db/dbRecursiveShapeIterator.h index 0afdd5423..95c2b0e99 100644 --- a/src/db/db/dbRecursiveShapeIterator.h +++ b/src/db/db/dbRecursiveShapeIterator.h @@ -391,6 +391,30 @@ public: } } + /** + * @brief Sets a global transformation + * + * The global transformation will be applied to all shapes delivered by biasing the "trans" attribute + */ + void set_global_trans (const cplx_trans_type &tr); + + /** + * @brief Gets the global transformation + */ + cplx_trans_type global_trans () const + { + return m_global_trans; + } + + /** + * @brief Gets the transformation which is to be applied always in push mode + * + * The reasoning behind this method is that in push mode and with the presence of a global transformation we need to + * somehow reflect the fact that the top-level is transformed. Instead of transforming every shape and instance we use + * this attribute. It is unity for all cells below top level and equal to the global transformation for the top cell. + */ + const cplx_trans_type &always_apply () const; + /** * @brief Reset the iterator */ @@ -727,6 +751,7 @@ private: bool m_shape_inv_prop_sel; bool m_overlapping; std::set m_start, m_stop; + cplx_trans_type m_global_trans; const layout_type *mp_layout; const cell_type *mp_top_cell; @@ -881,7 +906,7 @@ public: * - NI_single: iterate a single member (the first one) * - NI_skip: skips the whole array (not a single instance is iterated) */ - virtual new_inst_mode new_inst (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { return NI_all; } + virtual new_inst_mode new_inst (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { return NI_all; } /** * @brief Enters a new array member of the instance @@ -894,14 +919,14 @@ public: * * If this method returns false, this array instance (but not the whole array) is skipped and the cell is not entered. */ - virtual bool new_inst_member (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { return true; } + virtual bool new_inst_member (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*always_apply*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { return true; } /** * @brief Delivers a shape * * @param trans The transformation which maps the shape to the top cell. */ - virtual void shape (const RecursiveShapeIterator * /*iter*/, const db::Shape & /*shape*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) { } + virtual void shape (const RecursiveShapeIterator * /*iter*/, const db::Shape & /*shape*/, const db::ICplxTrans & /*always_apply*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) { } }; } // namespace db diff --git a/src/db/db/dbRegion.cc b/src/db/db/dbRegion.cc index 1cacb17f3..d01d37c2b 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -145,6 +145,8 @@ Region &Region::transform (const T &trans) template DB_PUBLIC Region &Region::transform (const db::ICplxTrans &); template DB_PUBLIC Region &Region::transform (const db::Trans &); template DB_PUBLIC Region &Region::transform (const db::Disp &); +template DB_PUBLIC Region &Region::transform (const db::IMatrix2d &); +template DB_PUBLIC Region &Region::transform (const db::IMatrix3d &); template void Region::insert (const Sh &shape) diff --git a/src/db/db/dbTilingProcessor.cc b/src/db/db/dbTilingProcessor.cc index a68fff914..cb85ea1ab 100644 --- a/src/db/db/dbTilingProcessor.cc +++ b/src/db/db/dbTilingProcessor.cc @@ -327,11 +327,14 @@ public: : tl::JobBase (nworkers), mp_proc (proc), m_has_tiles (has_tiles), - m_progress (0) + m_progress_count (0), + m_progress (std::string ()) { // .. nothing yet .. } + void start (const std::string &job_description); + bool has_tiles () const { return m_has_tiles; @@ -340,18 +343,18 @@ public: void next_progress () { tl::MutexLocker locker (&m_mutex); - ++m_progress; + ++m_progress_count; } - void update_progress (tl::RelativeProgress &progress) + void update_progress () { unsigned int p; { tl::MutexLocker locker (&m_mutex); - p = m_progress; + p = m_progress_count; } - progress.set (p, true /*force yield*/); + m_progress.set (p, true /*force yield*/); } TilingProcessor *processor () const @@ -361,11 +364,14 @@ public: virtual tl::Worker *create_worker (); + virtual void after_sync_task (tl::Task *task); + private: TilingProcessor *mp_proc; bool m_has_tiles; - unsigned int m_progress; + unsigned int m_progress_count; tl::Mutex m_mutex; + tl::RelativeProgress m_progress; }; class TilingProcessorTask @@ -601,9 +607,28 @@ TilingProcessorJob::create_worker () return new TilingProcessorWorker (this); } +void +TilingProcessorJob::after_sync_task (tl::Task * /*task*/) +{ + // This needs to be done here as there is no external loop to do this + update_progress (); +} + +void +TilingProcessorJob::start (const std::string &job_description) +{ + m_progress = tl::RelativeProgress (job_description, tasks (), 1); + // prevents child progress objects from showing + m_progress.set_final (true); + + tl::JobBase::start (); +} + // ---------------------------------------------------------------------------------- // The tiling processor implementation +tl::Mutex TilingProcessor::s_output_lock; + TilingProcessor::TilingProcessor () : m_tile_width (0.0), m_tile_height (0.0), m_ntiles_w (0), m_ntiles_h (0), @@ -781,7 +806,7 @@ TilingProcessor::output (const std::string &name, db::Edges &edges) tl::Variant TilingProcessor::receiver (const std::vector &args) { - tl::MutexLocker locker (&m_output_mutex); + tl::MutexLocker locker (&s_output_lock); if (args.size () != 1) { throw tl::Exception (tl::to_string (tr ("_rec function requires one argument: the handle of the output channel"))); @@ -803,7 +828,7 @@ TilingProcessor::receiver (const std::vector &args) void TilingProcessor::put (size_t ix, size_t iy, const db::Box &tile, const std::vector &args) { - tl::MutexLocker locker (&m_output_mutex); + tl::MutexLocker locker (&s_output_lock); if (args.size () < 2 || args.size () > 3) { throw tl::Exception (tl::to_string (tr ("_output function requires two or three arguments: handle and object and a clip flag (optional)"))); @@ -930,11 +955,6 @@ TilingProcessor::execute (const std::string &desc) } - // TODO: there should be a general scheme of how thread-specific progress is merged - // into a global one .. - size_t todo_count = ntiles_w * ntiles_h * m_scripts.size (); - tl::RelativeProgress progress (desc, todo_count, 1); - try { try { @@ -946,10 +966,10 @@ TilingProcessor::execute (const std::string &desc) } } - job.start (); + job.start (desc); while (job.is_running ()) { // This may throw an exception, if the cancel button has been pressed. - job.update_progress (progress); + job.update_progress (); job.wait (100); } diff --git a/src/db/db/dbTilingProcessor.h b/src/db/db/dbTilingProcessor.h index 3aabe4b00..35decec90 100644 --- a/src/db/db/dbTilingProcessor.h +++ b/src/db/db/dbTilingProcessor.h @@ -644,6 +644,14 @@ public: */ void execute (const std::string &desc); + /** + * @brief Gets the output mutex for operations not using the output method + */ + static tl::Mutex &output_lock () + { + return s_output_lock; + } + private: friend class TilingProcessorWorker; friend class TilingProcessorOutputFunction; @@ -689,8 +697,8 @@ private: bool m_dbu_specific_set; bool m_scale_to_dbu; std::vector m_scripts; - tl::Mutex m_output_mutex; tl::Eval m_top_eval; + static tl::Mutex s_output_lock; }; } diff --git a/src/db/db/gsiDeclDbBox.cc b/src/db/db/gsiDeclDbBox.cc index e6c8c8a0a..28b1079ea 100644 --- a/src/db/db/gsiDeclDbBox.cc +++ b/src/db/db/gsiDeclDbBox.cc @@ -101,6 +101,11 @@ struct box_defs return std::hfunc (*box); } + static const C &bbox (const C *box) + { + return *box; + } + static gsi::Methods methods () { return @@ -172,6 +177,12 @@ struct box_defs method ("p2=", &C::set_p2, gsi::arg ("p"), "@brief Sets the upper right point of the box\n" ) + + method_ext ("bbox", &bbox, + "@brief Returns the bounding box\n" + "This method is provided for consistency of the shape API is returns the box itself.\n" + "\n" + "This method has been introduced in version 0.27." + ) + method_ext ("contains?", &box_defs::contains, gsi::arg ("x"), gsi::arg ("y"), "@brief Returns true if the box contains the given point\n" "\n" diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index de891d776..6681da3f8 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -1297,22 +1297,24 @@ static void move_tree_shapes3 (db::Cell *cell, db::Cell &source_cell, const db:: } static void -fill_region1 (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Point *origin) +fill_region (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Point *origin, + db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box) { - if (fc_box.empty () || fc_box.width () == 0 || fc_box.height () == 0) { - throw tl::Exception (tl::to_string (tr ("Invalid fill cell footprint (empty or zero width/height)"))); - } - db::fill_region (cell, fr, fill_cell_index, fc_box, origin ? *origin : db::Point (), origin == 0, 0, db::Vector (), 0); + db::fill_region (cell, fr, fill_cell_index, fc_box, origin ? *origin : db::Point (), origin == 0, remaining_parts, fill_margin, remaining_polygons, glue_box); } static void -fill_region2 (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Point *origin, - db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons) +fill_region_skew (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Vector &row_step, const db::Vector &column_step, const db::Point *origin, + db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box) { - if (fc_box.empty () || fc_box.width () == 0 || fc_box.height () == 0) { - throw tl::Exception (tl::to_string (tr ("Invalid fill cell footprint (empty or zero width/height)"))); - } - db::fill_region (cell, fr, fill_cell_index, fc_box, origin ? *origin : db::Point (), origin == 0, remaining_parts, fill_margin, remaining_polygons); + db::fill_region (cell, fr, fill_cell_index, fc_box, row_step, column_step, origin ? *origin : db::Point (), origin == 0, remaining_parts, fill_margin, remaining_polygons, glue_box); +} + +static void +fill_region_multi (db::Cell *cell, const db::Region &fr, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Vector &row_step, const db::Vector &column_step, + const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Point &origin, const db::Box &glue_box) +{ + db::fill_region_repeat (cell, fr, fill_cell_index, fc_box, row_step, column_step, fill_margin, remaining_polygons, origin, glue_box); } static db::Instance cell_inst_dtransform_simple (db::Cell *cell, const db::Instance &inst, const db::DTrans &t) @@ -1497,6 +1499,8 @@ static db::Cell *dup_cell (const db::Cell *cell) return new_cell; } +static db::Point default_origin; + Class decl_Cell ("db", "Cell", gsi::method ("name", &db::Cell::get_basic_name, "@brief Gets the cell's name\n" @@ -1770,26 +1774,14 @@ Class decl_Cell ("db", "Cell", "\n" "This method has been introduced in version 0.23.\n" ) + - gsi::method_ext ("fill_region", &fill_region1, gsi::arg ("region"), gsi::arg ("fill_cell_index"), gsi::arg ("fc_box"), gsi::arg ("origin"), - "@brief Fills the given region with cells of the given type\n" - "@param region The region to fill\n" - "@param fill_cell_index The fill cell to place\n" - "@param fc_box The fill cell's footprint\n" - "@param origin The global origin of the fill pattern or nil to allow local (per-polygon) optimization\n" - "\n" - "This method creates a regular pattern of fill cells to cover the interior of the given region as far as possible. " - "This process is also known as tiling. The current implementation supports rectangular (not necessarily square) tile cells. " - "The tile cell's footprint is given by the fc_box parameter and the cells will be arranged with their footprints forming " - "a seamless array.\n" - "\n" - "The algorithm supports a global fill raster as well as local (per-polygon) origin optimization. In the latter case " - "the origin of the regular raster is optimized per individual polygon of the fill region.\n" - "\n" - "A more elaborate version of this method is available which also returns information about the non-filled parts.\n" - "\n" - "This method has been introduced in version 0.23.\n" - ) + - gsi::method_ext ("fill_region", &fill_region2, gsi::arg ("region"), gsi::arg ("fill_cell_index"), gsi::arg ("fc_box"), gsi::arg ("origin"), gsi::arg ("remaining_parts"), gsi::arg ("fill_margin"), gsi::arg ("remaining_polygons"), + gsi::method_ext ("fill_region", &fill_region, gsi::arg ("region"), + gsi::arg ("fill_cell_index"), + gsi::arg ("fc_box"), + gsi::arg ("origin", &default_origin, "(0, 0)"), + gsi::arg ("remaining_parts", (db::Region *)0, "nil"), + gsi::arg ("fill_margin", db::Vector ()), + gsi::arg ("remaining_polygons", (db::Region *)0, "nil"), + gsi::arg ("glue_box", db::Box ()), "@brief Fills the given region with cells of the given type (extended version)\n" "@param region The region to fill\n" "@param fill_cell_index The fill cell to place\n" @@ -1798,10 +1790,21 @@ Class decl_Cell ("db", "Cell", "@param remaining_parts See explanation below\n" "@param fill_margin See explanation below\n" "@param remaining_polygons See explanation below\n" + "@param glue_box Guarantees fill cell compatibility to neighbor regions in enhanced mode\n" "\n" - "First of all, this method behaves like the simple form. In addition, it can be configured to return information about the " - "parts which could not be filled. Those can be full polygons from the input (without a chance to fill) or parts of original polygons " - "which are worth being fed into the fill algorithm again.\n" + "This method creates a regular pattern of fill cells to cover the interior of the given region as far as possible. " + "This process is also known as tiling. This implementation supports rectangular (not necessarily square) tile cells. " + "The tile cell's footprint is given by the fc_box parameter and the cells will be arranged with their footprints forming " + "a seamless array.\n" + "\n" + "The algorithm supports a global fill raster as well as local (per-polygon) origin optimization. In the latter case " + "the origin of the regular raster is optimized per individual polygon of the fill region. To enable optimization, pass 'nil' to " + "the 'origin' argument.\n" + "\n" + "The implementation will basically try to find a repetition pattern of the tile cell's footprint " + "and produce instances which fit entirely into the fill region.\n" + "\n" + "There is also a version available which offers skew step vectors as a generalization of the orthogonal ones.\n" "\n" "If the 'remaining_parts' argument is non-nil, the corresponding region will receive the parts of the polygons which are not " "covered by tiles. Basically the tiles are subtracted from the original polygons. A margin can be specified which is applied " @@ -1830,7 +1833,65 @@ Class decl_Cell ("db", "Cell", "end\n" "@/code\n" "\n" - "This method has been introduced in version 0.23.\n" + "The glue box parameter supports fill cell array compatibility with neighboring regions. This is specifically useful when putting the fill_cell " + "method into a tiling processor. Fill cell array compatibility means that the fill cell array continues over tile boundaries. This is easy with an origin: " + "you can chose the origin identically over all tiles which is sufficient to guarantee fill cell array compatibility across the tiles. " + "However there is no freedom of choice of the origin then and fill cell placement may not be optimal. To enable the origin for the tile boundary only, " + "a glue box can given. The origin will then be used only when the polygons to fill not entirely inside and not at the border of the glue box. Hence, " + "while a certain degree of freedom is present for the placement of fill cells inside the glue box, the fill cells are guaranteed to be placed " + "at the raster implied by origin at the glue box border and beyond. To ensure fill cell compatibility inside the tiling processor, it is sufficient to use the tile " + "box as the glue box.\n" + "\n" + "This method has been introduced in version 0.23 and enhanced in version 0.27.\n" + ) + + gsi::method_ext ("fill_region", &fill_region_skew, gsi::arg ("region"), + gsi::arg ("fill_cell_index"), + gsi::arg ("fc_bbox"), + gsi::arg ("row_step"), + gsi::arg ("column_step"), + gsi::arg ("origin", &default_origin, "(0, 0)"), + gsi::arg ("remaining_parts", (db::Region *)0, "nil"), + gsi::arg ("fill_margin", db::Vector ()), + gsi::arg ("remaining_polygons", (db::Region *)0, "nil"), + gsi::arg ("glue_box", db::Box ()), + "@brief Fills the given region with cells of the given type (skew step version)\n" + "@param region The region to fill\n" + "@param fill_cell_index The fill cell to place\n" + "@param fc_bbox The fill cell's box to place\n" + "@param row_step The 'rows' step vector\n" + "@param column_step The 'columns' step vector\n" + "@param origin The global origin of the fill pattern or nil to allow local (per-polygon) optimization\n" + "@param remaining_parts See explanation in other version\n" + "@param fill_margin See explanation in other version\n" + "@param remaining_polygons See explanation in other version\n" + "\n" + "This version is similar to the version providing an orthogonal fill, but it offers more generic stepping of the fill cell.\n" + "The step pattern is defined by an origin and two vectors (row_step and column_step) which span the axes of the fill cell pattern.\n" + "\n" + "The fill box and the step vectors are decoupled which means the fill box can be larger or smaller than the step pitch - it can " + "be overlapping and there can be space between the fill box instances. Fill boxes are placed where they fit entirely into a polygon of the region. " + "The fill boxes lower left corner is the reference for the fill pattern and aligns with the origin if given.\n" + "\n" + "This variant has been introduced in version 0.27.\n" + ) + + gsi::method_ext ("fill_region_multi", &fill_region_multi, gsi::arg ("region"), + gsi::arg ("fill_cell_index"), + gsi::arg ("fc_bbox"), + gsi::arg ("row_step"), + gsi::arg ("column_step"), + gsi::arg ("fill_margin", db::Vector ()), + gsi::arg ("remaining_polygons", (db::Region *)0, "nil"), + gsi::arg ("origin", db::Point ()), + gsi::arg ("glue_box", db::Box ()), + "@brief Fills the given region with cells of the given type in enhanced mode with iterations\n" + "This version operates like \\fill_region, but repeats the fill generation until no further fill cells can be placed. " + "As the fill pattern origin changes between the iterations, narrow regions can be filled which cannot with a fixed fill pattern origin. " + "The \\fill_margin parameter is important as it controls the distance between fill cells with a different origin and therefore " + "introduces a safety distance between pitch-incompatible arrays.\n" + "\n" + "The origin is ignored unless a glue box is given. See \\fill_region for a description of this concept.\n" + "\n" + "This method has been introduced in version 0.27.\n" ) + gsi::method_ext ("begin_shapes_rec", &begin_shapes_rec, gsi::arg ("layer"), "@brief Delivers a recursive shape iterator for the shapes below the cell on the given layer\n" diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index 8db7d345e..9dc661e15 100644 --- a/src/db/db/gsiDeclDbEdgePairs.cc +++ b/src/db/db/gsiDeclDbEdgePairs.cc @@ -398,6 +398,30 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "@return The transformed edge pairs.\n" ) + + method ("transformed", (db::EdgePairs (db::EdgePairs::*)(const db::IMatrix2d &) const) &db::EdgePairs::transformed, gsi::arg ("t"), + "@brief Transform the edge pair collection\n" + "\n" + "Transforms the edge pairs with the given 2d matrix transformation.\n" + "Does not modify the edge pair collection but returns the transformed edge pairs.\n" + "\n" + "@param t The transformation to apply.\n" + "\n" + "@return The transformed edge pairs.\n" + "\n" + "This variant has been introduced in version 0.27.\n" + ) + + method ("transformed", (db::EdgePairs (db::EdgePairs::*)(const db::IMatrix3d &) const) &db::EdgePairs::transformed, gsi::arg ("t"), + "@brief Transform the edge pair collection\n" + "\n" + "Transforms the edge pairs with the given 3d matrix transformation.\n" + "Does not modify the edge pair collection but returns the transformed edge pairs.\n" + "\n" + "@param t The transformation to apply.\n" + "\n" + "@return The transformed edge pairs.\n" + "\n" + "This variant has been introduced in version 0.27.\n" + ) + method ("transform", (db::EdgePairs &(db::EdgePairs::*)(const db::Trans &)) &db::EdgePairs::transform, gsi::arg ("t"), "@brief Transform the edge pair collection (modifies self)\n" "\n" @@ -418,6 +442,30 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "@return The transformed edge pair collection.\n" ) + + method ("transform", (db::EdgePairs &(db::EdgePairs::*)(const db::IMatrix2d &)) &db::EdgePairs::transform, gsi::arg ("t"), + "@brief Transform the edge pair collection (modifies self)\n" + "\n" + "Transforms the edge pair collection with the given 2d matrix transformation.\n" + "This version modifies the edge pair collection and returns a reference to self.\n" + "\n" + "@param t The transformation to apply.\n" + "\n" + "@return The transformed edge pair collection.\n" + "\n" + "This variant has been introduced in version 0.27.\n" + ) + + method ("transform", (db::EdgePairs &(db::EdgePairs::*)(const db::IMatrix3d &)) &db::EdgePairs::transform, gsi::arg ("t"), + "@brief Transform the edge pair collection (modifies self)\n" + "\n" + "Transforms the edge pair collection with the given 3d matrix transformation.\n" + "This version modifies the edge pair collection and returns a reference to self.\n" + "\n" + "@param t The transformation to apply.\n" + "\n" + "@return The transformed edge pair collection.\n" + "\n" + "This variant has been introduced in version 0.27.\n" + ) + method_ext ("insert", &insert_e, gsi::arg ("edge_pairs"), "@brief Inserts all edge pairs from the other edge pair collection into this edge pair collection\n" "This method has been introduced in version 0.25." diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index 9cf71b45a..653481789 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -1040,6 +1040,30 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "\n" "@return The transformed edge collection.\n" ) + + method ("transformed", (db::Edges (db::Edges::*)(const db::IMatrix2d &) const) &db::Edges::transformed, gsi::arg ("t"), + "@brief Transform the edge collection\n" + "\n" + "Transforms the edge collection with the given 2d matrix transformation.\n" + "Does not modify the edge collection but returns the transformed edge collection.\n" + "\n" + "@param t The transformation to apply.\n" + "\n" + "@return The transformed edge collection.\n" + "\n" + "This variant has been introduced in version 0.27." + ) + + method ("transformed", (db::Edges (db::Edges::*)(const db::IMatrix3d &) const) &db::Edges::transformed, gsi::arg ("t"), + "@brief Transform the edge collection\n" + "\n" + "Transforms the edge collection with the given 3d matrix transformation.\n" + "Does not modify the edge collection but returns the transformed edge collection.\n" + "\n" + "@param t The transformation to apply.\n" + "\n" + "@return The transformed edge collection.\n" + "\n" + "This variant has been introduced in version 0.27." + ) + method ("transform", (db::Edges &(db::Edges::*)(const db::Trans &)) &db::Edges::transform, gsi::arg ("t"), "@brief Transform the edge collection (modifies self)\n" "\n" @@ -1060,6 +1084,30 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "\n" "@return The transformed edge collection.\n" ) + + method ("transform", (db::Edges &(db::Edges::*)(const db::IMatrix2d &)) &db::Edges::transform, gsi::arg ("t"), + "@brief Transform the edge collection (modifies self)\n" + "\n" + "Transforms the edge collection with the given 2d matrix transformation.\n" + "This version modifies the edge collection and returns a reference to self.\n" + "\n" + "@param t The transformation to apply.\n" + "\n" + "@return The transformed edge collection.\n" + "\n" + "This variant has been introduced in version 0.27." + ) + + method ("transform", (db::Edges &(db::Edges::*)(const db::IMatrix3d &)) &db::Edges::transform, gsi::arg ("t"), + "@brief Transform the edge collection (modifies self)\n" + "\n" + "Transforms the edge collection with the given 3d matrix transformation.\n" + "This version modifies the edge collection and returns a reference to self.\n" + "\n" + "@param t The transformation to apply.\n" + "\n" + "@return The transformed edge collection.\n" + "\n" + "This variant has been introduced in version 0.27." + ) + method_ext ("width_check", &width2, gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), "@brief Performs a width check with options\n" "@param d The minimum width for which the edges are checked\n" diff --git a/src/db/db/gsiDeclDbMatrix.cc b/src/db/db/gsiDeclDbMatrix.cc index 07f9edefa..5585cbcee 100644 --- a/src/db/db/gsiDeclDbMatrix.cc +++ b/src/db/db/gsiDeclDbMatrix.cc @@ -24,6 +24,9 @@ #include "gsiDecl.h" #include "dbMatrix.h" #include "dbTrans.h" +#include "dbBox.h" +#include "dbPolygon.h" +#include "dbEdge.h" namespace gsi { @@ -31,62 +34,104 @@ namespace gsi // --------------------------------------------------------------- // Matrix2d binding -static db::Matrix2d *new_matrix2d () +template +static db::matrix_2d *new_matrix2d () { - return new db::Matrix2d (1.0); + return new db::matrix_2d (1.0); } -static db::Matrix2d *new_matrix2d_m (double mag) +template +static db::matrix_2d *new_matrix2d_m (double mag) { - return new db::Matrix2d (mag); + return new db::matrix_2d (mag); } -static db::Matrix2d *new_matrix2d_m2 (double mx, double my) +template +static db::matrix_2d *new_matrix2d_m2 (double mx, double my) { - return new db::Matrix2d (mx, my); + return new db::matrix_2d (mx, my); } -static db::Matrix2d *new_matrix2d_t (const db::DCplxTrans &t) +template +static db::matrix_2d *new_matrix2d_t (const db::DCplxTrans &t) { - return new db::Matrix2d (t); + return new db::matrix_2d (t); } -static db::Matrix2d *new_matrix2d_mrm (double mag, double rot, bool m) +template +static db::matrix_2d *new_matrix2d_mrm (double mag, double rot, bool m) { - return new db::Matrix2d (db::Matrix2d::rotation (rot) * db::Matrix2d::mag (mag) * db::Matrix2d::mirror (m)); + return new db::matrix_2d (db::matrix_2d::rotation (rot) * db::matrix_2d::mag (mag) * db::matrix_2d::mirror (m)); } -static db::Matrix2d *new_matrix2d_smrm (double shear, double mx, double my, double rot, bool m) +template +static db::matrix_2d *new_matrix2d_smrm (double shear, double mx, double my, double rot, bool m) { - return new db::Matrix2d (db::Matrix2d::rotation (rot) * db::Matrix2d::shear (shear) * db::Matrix2d::mag (mx, my) * db::Matrix2d::mirror (m)); + return new db::matrix_2d (db::matrix_2d::rotation (rot) * db::matrix_2d::shear (shear) * db::matrix_2d::mag (mx, my) * db::matrix_2d::mirror (m)); } -static db::Matrix2d *new_matrix2d_m4 (double m11, double m12, double m21, double m22) +template +static db::matrix_2d *new_matrix2d_m4 (double m11, double m12, double m21, double m22) { - return new db::Matrix2d (m11, m12, m21, m22); + return new db::matrix_2d (m11, m12, m21, m22); } -static db::DCplxTrans to_cplx_trans (const db::Matrix2d *m) +template +static db::complex_trans to_cplx_trans (const db::matrix_2d *m) { - return db::DCplxTrans (db::Matrix3d (*m)); + return db::complex_trans (db::matrix_3d (*m)); } -static db::Matrix2d sum_m (const db::Matrix2d *m, const db::Matrix2d &d) +template +static db::matrix_2d sum_m (const db::matrix_2d *m, const db::matrix_2d &d) { return *m + d; } -static db::Matrix2d prod_m (const db::Matrix2d *m, const db::Matrix2d &d) +template +static db::matrix_2d prod_m (const db::matrix_2d *m, const db::matrix_2d &d) { return *m * d; } -static db::DPoint trans_p (const db::Matrix2d *m, const db::DPoint &p) +template +static db::point trans_p (const db::matrix_2d *m, const db::point &p) { return *m * p; } -static double coeff_m (const db::Matrix2d *m, int i, int j) +template +static db::vector trans_v (const db::matrix_2d *m, const db::vector &p) +{ + return *m * p; +} + +template +static db::polygon trans_polygon (const db::matrix_2d *m, const db::polygon &p) +{ + return p.transformed (*m); +} + +template +static db::simple_polygon trans_simple_polygon (const db::matrix_2d *m, const db::simple_polygon &p) +{ + return p.transformed (*m); +} + +template +static db::box trans_box (const db::matrix_2d *m, const db::box &p) +{ + return p.transformed (*m); +} + +template +static db::edge trans_edge (const db::matrix_2d *m, const db::edge &e) +{ + return e.transformed (*m); +} + +template +static double coeff_m (const db::matrix_2d *m, int i, int j) { if (i == 0 && j == 0) { return m->m11 (); @@ -101,211 +146,307 @@ static double coeff_m (const db::Matrix2d *m, int i, int j) } } +template +gsi::Methods +matrix2d_methods () +{ + return + gsi::constructor ("new", &new_matrix2d, + "@brief Create a new Matrix2d representing a unit transformation" + ) + + gsi::constructor ("new", &new_matrix2d_m, gsi::arg ("m"), + "@brief Create a new Matrix2d representing an isotropic magnification\n" + "@param m The magnification\n" + ) + + gsi::constructor ("new", &new_matrix2d_m2, gsi::arg ("mx"), gsi::arg ("my"), + "@brief Create a new Matrix2d representing an anisotropic magnification\n" + "@param mx The magnification in x direction\n" + "@param my The magnification in y direction\n" + ) + + gsi::constructor ("new", &new_matrix2d_t, gsi::arg ("t"), + "@brief Create a new Matrix2d from the given complex transformation" + "@param t The transformation from which to create the matrix (not taking into account the displacement)\n" + ) + + gsi::constructor ("newc", &new_matrix2d_mrm, gsi::arg ("mag"), gsi::arg ("rotation"), gsi::arg ("mirror"), + "@brief Create a new Matrix2d representing an isotropic magnification, rotation and mirroring\n" + "@param mag The magnification in x direction\n" + "@param rotation The rotation angle (in degree)\n" + "@param mirror The mirror flag (at x axis)\n" + "\n" + "This constructor is provided to construct a matrix similar to the complex transformation.\n" + "This constructor is called 'newc' to distinguish it from the constructors taking matrix coefficients ('c' is for composite).\n" + "The order of execution of the operations is mirror, magnification, rotation (as for complex transformations).\n" + ) + + gsi::constructor ("newc", &new_matrix2d_smrm, gsi::arg ("shear"), gsi::arg ("mx"), gsi::arg ("my"), gsi::arg ("rotation"), gsi::arg ("mirror"), + "@brief Create a new Matrix2d representing a shear, anisotropic magnification, rotation and mirroring\n" + "@param shear The shear angle\n" + "@param mx The magnification in x direction\n" + "@param my The magnification in y direction\n" + "@param rotation The rotation angle (in degree)\n" + "@param mirror The mirror flag (at x axis)\n" + "\n" + "The order of execution of the operations is mirror, magnification, shear and rotation.\n" + "This constructor is called 'newc' to distinguish it from the constructor taking the four matrix coefficients ('c' is for composite).\n" + ) + + gsi::constructor ("new", &new_matrix2d_m4, gsi::arg ("m11"), gsi::arg ("m12"), gsi::arg ("m21"), gsi::arg ("m22"), + "@brief Create a new Matrix2d from the four coefficients\n" + ) + + gsi::method ("m11", &db::matrix_2d::m11, + "@brief Gets the m11 coefficient.\n" + "@return The value of the m11 coefficient\n" + ) + + gsi::method ("m12", &db::matrix_2d::m12, + "@brief Gets the m12 coefficient.\n" + "@return The value of the m12 coefficient\n" + ) + + gsi::method ("m21", &db::matrix_2d::m21, + "@brief Gets the m21 coefficient.\n" + "@return The value of the m21 coefficient\n" + ) + + gsi::method ("m22", &db::matrix_2d::m22, + "@brief Gets the m22 coefficient.\n" + "@return The value of the m22 coefficient\n" + ) + + gsi::method_ext ("m", &coeff_m, gsi::arg ("i"), gsi::arg ("j"), + "@brief Gets the m coefficient with the given index.\n" + "@return The coefficient [i,j]\n" + ) + + gsi::method ("to_s", &db::matrix_2d::to_string, + "@brief Convert the matrix to a string.\n" + "@return The string representing this matrix\n" + ) + + gsi::method ("inverted", &db::matrix_2d::inverted, + "@brief The inverse of this matrix.\n" + "@return The inverse of this matrix\n" + ) + + gsi::method_ext ("trans|*", &trans_p, gsi::arg ("p"), + "@brief Transforms a point with this matrix.\n" + "@param p The point to transform.\n" + "@return The transformed point\n" + ) + + gsi::method_ext ("*", &trans_v, gsi::arg ("v"), + "@brief Transforms a vector with this matrix.\n" + "@param v The vector to transform.\n" + "@return The transformed vector\n" + ) + + gsi::method_ext ("*", &trans_edge, gsi::arg ("e"), + "@brief Transforms an edge with this matrix.\n" + "@param e The edge to transform.\n" + "@return The transformed edge\n" + ) + + gsi::method_ext ("*", &trans_box, gsi::arg ("box"), + "@brief Transforms a box with this matrix.\n" + "@param box The box to transform.\n" + "@return The transformed box\n" + "\n" + "Please note that the box remains a box, even though the matrix supports shear and rotation. The returned box " + "will be the bounding box of the sheared and rotated rectangle." + ) + + gsi::method_ext ("*", &trans_simple_polygon, gsi::arg ("p"), + "@brief Transforms a simple polygon with this matrix.\n" + "@param p The simple polygon to transform.\n" + "@return The transformed simple polygon\n" + ) + + gsi::method_ext ("*", &trans_polygon, gsi::arg ("p"), + "@brief Transforms a polygon with this matrix.\n" + "@param p The polygon to transform.\n" + "@return The transformed polygon\n" + ) + + gsi::method_ext ("*", &prod_m, gsi::arg ("m"), + "@brief Product of two matrices.\n" + "@param m The other matrix.\n" + "@return The matrix product self*m\n" + ) + + gsi::method_ext ("+", &sum_m, gsi::arg ("m"), + "@brief Sum of two matrices.\n" + "@param m The other matrix.\n" + "@return The (element-wise) sum of self+m\n" + ) + + gsi::method_ext ("cplx_trans", &to_cplx_trans, + "@brief Converts this matrix to a complex transformation (if possible).\n" + "@return The complex transformation.\n" + "This method is successful only if the matrix does not contain shear components and the magnification must be isotropic.\n" + ) + + gsi::method ("angle", &db::matrix_2d::angle, + "@brief Returns the rotation angle of the rotation component of this matrix.\n" + "@return The angle in degree.\n" + "The matrix is decomposed into basic transformations assuming an execution order of " + "mirroring at the x axis, rotation, magnification and shear." + ) + + gsi::method ("mag_x", (double (db::matrix_2d::*) () const) &db::matrix_2d::mag_x, + "@brief Returns the x magnification of the magnification component of this matrix.\n" + "@return The magnification factor.\n" + "The matrix is decomposed into basic transformations assuming an execution order of " + "mirroring at the x axis, magnification, shear and rotation." + ) + + gsi::method ("mag_y", (double (db::matrix_2d::*) () const) &db::matrix_2d::mag_y, + "@brief Returns the y magnification of the magnification component of this matrix.\n" + "@return The magnification factor.\n" + "The matrix is decomposed into basic transformations assuming an execution order of " + "mirroring at the x axis, magnification, shear and rotation." + ) + + gsi::method ("shear_angle", &db::matrix_2d::shear_angle, + "@brief Returns the magnitude of the shear component of this matrix.\n" + "@return The shear angle in degree.\n" + "The matrix is decomposed into basic transformations assuming an execution order of " + "mirroring at the x axis, rotation, magnification and shear.\n" + "The shear basic transformation will tilt the x axis towards the y axis and vice versa. The shear angle " + "gives the tilt angle of the axes towards the other one. The possible range for this angle is -45 to 45 degree." + ) + + gsi::method ("is_mirror?", &db::matrix_2d::is_mirror, + "@brief Returns the mirror flag of this matrix.\n" + "@return True if this matrix has a mirror component.\n" + "The matrix is decomposed into basic transformations assuming an execution order of " + "mirroring at the x axis, rotation, magnification and shear." + ); +} + gsi::Class decl_Matrix2d ("db", "Matrix2d", - gsi::constructor ("new", &new_matrix2d, - "@brief Create a new Matrix2d representing a unit transformation" - ) + - gsi::constructor ("new", &new_matrix2d_m, gsi::arg ("m"), - "@brief Create a new Matrix2d representing an isotropic magnification\n" - "@param m The magnification\n" - ) + - gsi::constructor ("new", &new_matrix2d_m2, gsi::arg ("mx"), gsi::arg ("my"), - "@brief Create a new Matrix2d representing an anisotropic magnification\n" - "@param mx The magnification in x direction\n" - "@param my The magnification in y direction\n" - ) + - gsi::constructor ("new", &new_matrix2d_t, gsi::arg ("t"), - "@brief Create a new Matrix2d from the given complex transformation" - "@param t The transformation from which to create the matrix (not taking into account the displacement)\n" - ) + - gsi::constructor ("newc", &new_matrix2d_mrm, gsi::arg ("mag"), gsi::arg ("rotation"), gsi::arg ("mirror"), - "@brief Create a new Matrix2d representing an isotropic magnification, rotation and mirroring\n" - "@param mag The magnification in x direction\n" - "@param rotation The rotation angle (in degree)\n" - "@param mirror The mirror flag (at x axis)\n" - "\n" - "This constructor is provided to construct a matrix similar to the complex transformation.\n" - "This constructor is called 'newc' to distinguish it from the constructors taking matrix coefficients ('c' is for composite).\n" - "The order of execution of the operations is mirror, magnification, rotation (as for complex transformations).\n" - ) + - gsi::constructor ("newc", &new_matrix2d_smrm, gsi::arg ("shear"), gsi::arg ("mx"), gsi::arg ("my"), gsi::arg ("rotation"), gsi::arg ("mirror"), - "@brief Create a new Matrix2d representing a shear, anisotropic magnification, rotation and mirroring\n" - "@param shear The shear angle\n" - "@param mx The magnification in x direction\n" - "@param my The magnification in y direction\n" - "@param rotation The rotation angle (in degree)\n" - "@param mirror The mirror flag (at x axis)\n" - "\n" - "The order of execution of the operations is mirror, magnification, shear and rotation.\n" - "This constructor is called 'newc' to distinguish it from the constructor taking the four matrix coefficients ('c' is for composite).\n" - ) + - gsi::constructor ("new", &new_matrix2d_m4, gsi::arg ("m11"), gsi::arg ("m12"), gsi::arg ("m21"), gsi::arg ("m22"), - "@brief Create a new Matrix2d from the four coefficients\n" - ) + - gsi::method ("m11", &db::Matrix2d::m11, - "@brief Gets the m11 coefficient.\n" - "@return The value of the m11 coefficient\n" - ) + - gsi::method ("m12", &db::Matrix2d::m12, - "@brief Gets the m12 coefficient.\n" - "@return The value of the m12 coefficient\n" - ) + - gsi::method ("m21", &db::Matrix2d::m21, - "@brief Gets the m21 coefficient.\n" - "@return The value of the m21 coefficient\n" - ) + - gsi::method ("m22", &db::Matrix2d::m22, - "@brief Gets the m22 coefficient.\n" - "@return The value of the m22 coefficient\n" - ) + - gsi::method_ext ("m", &coeff_m, gsi::arg ("i"), gsi::arg ("j"), - "@brief Gets the m coefficient with the given index.\n" - "@return The coefficient [i,j]\n" - ) + - gsi::method ("to_s", &db::Matrix2d::to_string, - "@brief Convert the matrix to a string.\n" - "@return The string representing this matrix\n" - ) + - gsi::method ("inverted", &db::Matrix2d::inverted, - "@brief The inverse of this matrix.\n" - "@return The inverse of this matrix\n" - ) + - gsi::method_ext ("trans", &trans_p, gsi::arg ("p"), - "@brief Transforms a point with this matrix.\n" - "@param p The point to transform.\n" - "@return The product if self and the point p\n" - ) + - gsi::method_ext ("*", &prod_m, gsi::arg ("m"), - "@brief Product of two matrices.\n" - "@param m The other matrix.\n" - "@return The matrix product self*m\n" - ) + - gsi::method_ext ("+", &sum_m, gsi::arg ("m"), - "@brief Sum of two matrices.\n" - "@param m The other matrix.\n" - "@return The (element-wise) sum of self+m\n" - ) + - gsi::method_ext ("cplx_trans", &to_cplx_trans, - "@brief Converts this matrix to a complex transformation (if possible).\n" - "@return The complex transformation.\n" - "This method is successful only if the matrix does not contain shear components and the magnification must be isotropic.\n" - ) + - gsi::method ("angle", &db::Matrix2d::angle, - "@brief Returns the rotation angle of the rotation component of this matrix.\n" - "@return The angle in degree.\n" - "The matrix is decomposed into basic transformations assuming an execution order of " - "mirroring at the x axis, rotation, magnification and shear." - ) + - gsi::method ("mag_x", (double (db::Matrix2d::*) () const) &db::Matrix2d::mag_x, - "@brief Returns the x magnification of the magnification component of this matrix.\n" - "@return The magnification factor.\n" - "The matrix is decomposed into basic transformations assuming an execution order of " - "mirroring at the x axis, magnification, shear and rotation." - ) + - gsi::method ("mag_y", (double (db::Matrix2d::*) () const) &db::Matrix2d::mag_y, - "@brief Returns the y magnification of the magnification component of this matrix.\n" - "@return The magnification factor.\n" - "The matrix is decomposed into basic transformations assuming an execution order of " - "mirroring at the x axis, magnification, shear and rotation." - ) + - gsi::method ("shear_angle", &db::Matrix2d::shear_angle, - "@brief Returns the magnitude of the shear component of this matrix.\n" - "@return The shear angle in degree.\n" - "The matrix is decomposed into basic transformations assuming an execution order of " - "mirroring at the x axis, rotation, magnification and shear.\n" - "The shear basic transformation will tilt the x axis towards the y axis and vice versa. The shear angle " - "gives the tilt angle of the axes towards the other one. The possible range for this angle is -45 to 45 degree." - ) + - gsi::method ("is_mirror?", &db::Matrix2d::is_mirror, - "@brief Returns the mirror flag of this matrix.\n" - "@return True if this matrix has a mirror component.\n" - "The matrix is decomposed into basic transformations assuming an execution order of " - "mirroring at the x axis, rotation, magnification and shear." - ), + matrix2d_methods (), "@brief A 2d matrix object used mainly for representing rotation and shear transformations.\n" "\n" - "This object represents a 2x2 matrix. This matrix is used to represent affine transformations " + "This object represents a 2x2 matrix. This matrix is used to implement affine transformations " "in the 2d space mainly. It can be decomposed into basic transformations: mirroring, rotation and shear. " "In that case, the assumed execution order of the basic transformations is " - "mirroring at the x axis, rotation, magnification and shear." + "mirroring at the x axis, rotation, magnification and shear.\n" + "\n" + "The matrix is a generalisation of the transformations and is of limited use in a layout database context. " + "It is useful however to implement shear transformations on polygons, edges and polygon or edge collections." "\n\n" "This class was introduced in version 0.22.\n" ); +gsi::Class decl_IMatrix2d ("db", "IMatrix2d", + matrix2d_methods (), + "@brief A 2d matrix object used mainly for representing rotation and shear transformations (integer coordinate version).\n" + "\n" + "This object represents a 2x2 matrix. This matrix is used to implement affine transformations " + "in the 2d space mainly. It can be decomposed into basic transformations: mirroring, rotation and shear. " + "In that case, the assumed execution order of the basic transformations is " + "mirroring at the x axis, rotation, magnification and shear." + "\n\n" + "The integer variant was introduced in version 0.27.\n" +); + // --------------------------------------------------------------- -// Matrix2d binding +// Matrix3d binding -static db::Matrix3d *new_matrix3d () +template +static db::matrix_3d *new_matrix3d () { - return new db::Matrix3d (1.0); + return new db::matrix_3d (1.0); } -static db::Matrix3d *new_matrix3d_t (const db::DCplxTrans &t) +template +static db::matrix_3d *new_matrix3d_t (const db::complex_trans &t) { - return new db::Matrix3d (t); + return new db::matrix_3d (t); } -static db::Matrix3d *new_matrix3d_m (double mag) +template +static db::matrix_3d *new_matrix3d_m (double mag) { - return new db::Matrix3d (mag); + return new db::matrix_3d (mag); } -static db::Matrix3d *new_matrix3d_mrm (double mag, double rot, bool m) +template +static db::matrix_3d *new_matrix3d_mrm (double mag, double rot, bool m) { - return new db::Matrix3d (db::Matrix3d::rotation (rot) * db::Matrix3d::mag (mag) * db::Matrix3d::mirror (m)); + return new db::matrix_3d (db::matrix_3d::rotation (rot) * db::matrix_3d::mag (mag) * db::matrix_3d::mirror (m)); } -static db::Matrix3d *new_matrix3d_smrm (double shear, double mx, double my, double rot, bool m) +template +static db::matrix_3d *new_matrix3d_smrm (double shear, double mx, double my, double rot, bool m) { - return new db::Matrix3d (db::Matrix3d::rotation (rot) * db::Matrix3d::shear (shear) * db::Matrix3d::mag (mx, my) * db::Matrix3d::mirror (m)); + return new db::matrix_3d (db::matrix_3d::rotation (rot) * db::matrix_3d::shear (shear) * db::matrix_3d::mag (mx, my) * db::matrix_3d::mirror (m)); } -static db::Matrix3d *new_matrix3d_dsmrm (const db::DVector &d, double shear, double mx, double my, double rot, bool m) +template +static db::matrix_3d *new_matrix3d_dsmrm (const db::vector &d, double shear, double mx, double my, double rot, bool m) { - return new db::Matrix3d (db::Matrix3d::disp (d) * db::Matrix3d::rotation (rot) * db::Matrix3d::shear (shear) * db::Matrix3d::mag (mx, my) * db::Matrix3d::mirror (m)); + return new db::matrix_3d (db::matrix_3d::disp (d) * db::matrix_3d::rotation (rot) * db::matrix_3d::shear (shear) * db::matrix_3d::mag (mx, my) * db::matrix_3d::mirror (m)); } -static db::Matrix3d *new_matrix3d_pdsmrm (double tx, double ty, double z, const db::DVector &d, double shear, double mx, double my, double rot, bool m) +template +static db::matrix_3d *new_matrix3d_pdsmrm (double tx, double ty, double z, const db::vector &d, double shear, double mx, double my, double rot, bool m) { - return new db::Matrix3d (db::Matrix3d::disp (d) * db::Matrix3d::perspective (tx, ty, z) * db::Matrix3d::rotation (rot) * db::Matrix3d::shear (shear) * db::Matrix3d::mag (mx, my) * db::Matrix3d::mirror (m)); + return new db::matrix_3d (db::matrix_3d::disp (d) * db::matrix_3d::perspective (tx, ty, z) * db::matrix_3d::rotation (rot) * db::matrix_3d::shear (shear) * db::matrix_3d::mag (mx, my) * db::matrix_3d::mirror (m)); } -static db::Matrix3d *new_matrix3d_m4 (double m11, double m12, double m21, double m22) +template +static db::matrix_3d *new_matrix3d_m4 (double m11, double m12, double m21, double m22) { - return new db::Matrix3d (m11, m12, m21, m22); + return new db::matrix_3d (m11, m12, m21, m22); } -static db::Matrix3d *new_matrix3d_m6 (double m11, double m12, double m21, double m22, double dx, double dy) +template +static db::matrix_3d *new_matrix3d_m6 (double m11, double m12, double m21, double m22, double dx, double dy) { - return new db::Matrix3d (m11, m12, m21, m22, dx, dy, 0.0, 0.0); + return new db::matrix_3d (m11, m12, m21, m22, dx, dy, 0.0, 0.0); } -static db::Matrix3d *new_matrix3d_m9 (double m11, double m12, double m13, double m21, double m22, double m23, double m31, double m32, double m33) +template +static db::matrix_3d *new_matrix3d_m9 (double m11, double m12, double m13, double m21, double m22, double m23, double m31, double m32, double m33) { - return new db::Matrix3d (m11, m12, m13, m21, m22, m23, m31, m32, m33); + return new db::matrix_3d (m11, m12, m13, m21, m22, m23, m31, m32, m33); } -static db::DCplxTrans to_cplx_trans3 (const db::Matrix3d *m) +template +static db::DCplxTrans to_cplx_trans3 (const db::matrix_3d *m) { return db::DCplxTrans (*m); } -static db::Matrix3d sum_m3 (const db::Matrix3d *m, const db::Matrix3d &d) +template +static db::matrix_3d sum_m3 (const db::matrix_3d *m, const db::matrix_3d &d) { return *m + d; } -static db::Matrix3d prod_m3 (const db::Matrix3d *m, const db::Matrix3d &d) +template +static db::matrix_3d prod_m3 (const db::matrix_3d *m, const db::matrix_3d &d) { return *m * d; } -static db::DPoint trans_p3 (const db::Matrix3d *m, const db::DPoint &p) +template +static db::point trans_p3 (const db::matrix_3d *m, const db::point &p) { return *m * p; } -static double coeff_m3 (const db::Matrix3d *m, int i, int j) +template +static db::vector trans_v3 (const db::matrix_3d *m, const db::vector &p) +{ + return *m * p; +} + +template +static db::polygon trans_polygon3 (const db::matrix_3d *m, const db::polygon &p) +{ + return p.transformed (*m); +} + +template +static db::simple_polygon trans_simple_polygon3 (const db::matrix_3d *m, const db::simple_polygon &p) +{ + return p.transformed (*m); +} + +template +static db::box trans_box3 (const db::matrix_3d *m, const db::box &p) +{ + return p.transformed (*m); +} + +template +static db::edge trans_edge3 (const db::matrix_3d *m, const db::edge &e) +{ + return e.transformed (*m); +} + +template +static double coeff_m3 (const db::matrix_3d *m, int i, int j) { if (i < 0 || i >= 3 || j < 0 || j >= 3) { return 0.0; @@ -359,166 +500,198 @@ static int adjust_all () return db::MatrixAdjustFlags::All; } +template +gsi::Methods +matrix3d_methods () +{ + return + gsi::constructor ("new", &new_matrix3d, + "@brief Create a new Matrix3d representing a unit transformation" + ) + + gsi::constructor ("new", &new_matrix3d_m, gsi::arg ("m"), + "@brief Create a new Matrix3d representing a magnification\n" + "@param m The magnification\n" + ) + + gsi::constructor ("new", &new_matrix3d_t, gsi::arg ("t"), + "@brief Create a new Matrix3d from the given complex transformation" + "@param t The transformation from which to create the matrix\n" + ) + + gsi::constructor ("newc", &new_matrix3d_mrm, gsi::arg ("mag"), gsi::arg ("rotation"), gsi::arg ("mirrx"), + "@brief Create a new Matrix3d representing a isotropic magnification, rotation and mirroring\n" + "@param mag The magnification\n" + "@param rotation The rotation angle (in degree)\n" + "@param mirrx The mirror flag (at x axis)\n" + "\n" + "The order of execution of the operations is mirror, magnification and rotation.\n" + "This constructor is called 'newc' to distinguish it from the constructors taking coefficients ('c' is for composite).\n" + ) + + gsi::constructor ("newc", &new_matrix3d_smrm, gsi::arg ("shear"), gsi::arg ("mx"), gsi::arg ("my"), gsi::arg ("rotation"), gsi::arg ("mirrx"), + "@brief Create a new Matrix3d representing a shear, anisotropic magnification, rotation and mirroring\n" + "@param shear The shear angle\n" + "@param mx The magnification in x direction\n" + "@param mx The magnification in y direction\n" + "@param rotation The rotation angle (in degree)\n" + "@param mirrx The mirror flag (at x axis)\n" + "\n" + "The order of execution of the operations is mirror, magnification, rotation and shear.\n" + "This constructor is called 'newc' to distinguish it from the constructor taking the four matrix coefficients ('c' is for composite).\n" + ) + + gsi::constructor ("newc", &new_matrix3d_dsmrm, gsi::arg ("u"), gsi::arg ("shear"), gsi::arg ("mx"), gsi::arg ("my"), gsi::arg ("rotation"), gsi::arg ("mirrx"), + "@brief Create a new Matrix3d representing a displacement, shear, anisotropic magnification, rotation and mirroring\n" + "@param u The displacement\n" + "@param shear The shear angle\n" + "@param mx The magnification in x direction\n" + "@param mx The magnification in y direction\n" + "@param rotation The rotation angle (in degree)\n" + "@param mirrx The mirror flag (at x axis)\n" + "\n" + "The order of execution of the operations is mirror, magnification, rotation, shear and displacement.\n" + "This constructor is called 'newc' to distinguish it from the constructor taking the four matrix coefficients ('c' is for composite).\n" + "\n" + "Starting with version 0.25 the displacement is of vector type." + ) + + gsi::constructor ("newc", &new_matrix3d_pdsmrm, gsi::arg ("tx"), gsi::arg ("ty"), gsi::arg ("z"), gsi::arg ("u"), gsi::arg ("shear"), gsi::arg ("mx"), gsi::arg ("my"), gsi::arg ("rotation"), gsi::arg ("mirrx"), + "@brief Create a new Matrix3d representing a perspective distortion, displacement, shear, anisotropic magnification, rotation and mirroring\n" + "@param tx The perspective tilt angle x (around the y axis)\n" + "@param ty The perspective tilt angle y (around the x axis)\n" + "@param z The observer distance at which the tilt angles are given\n" + "@param u The displacement\n" + "@param shear The shear angle\n" + "@param mx The magnification in x direction\n" + "@param mx The magnification in y direction\n" + "@param rotation The rotation angle (in degree)\n" + "@param mirrx The mirror flag (at x axis)\n" + "\n" + "The order of execution of the operations is mirror, magnification, rotation, shear, perspective distortion and displacement.\n" + "This constructor is called 'newc' to distinguish it from the constructor taking the four matrix coefficients ('c' is for composite).\n" + "\n" + "The tx and ty parameters represent the perspective distortion. They denote a tilt of the xy plane around the y axis (tx) or the x axis (ty) in degree. " + "The same effect is achieved for different tilt angles for different observer distances. Hence, the observer distance must be given at which the tilt angles are given. " + "If the magnitude of the tilt angle is not important, z can be set to 1.\n" + "\n" + "Starting with version 0.25 the displacement is of vector type." + ) + + gsi::constructor ("new", &new_matrix3d_m4, gsi::arg ("m11"), gsi::arg ("m12"), gsi::arg ("m21"), gsi::arg ("m22"), + "@brief Create a new Matrix3d from the four coefficients of a Matrix2d\n" + ) + + gsi::constructor ("new", &new_matrix3d_m6, gsi::arg ("m11"), gsi::arg ("m12"), gsi::arg ("m21"), gsi::arg ("m22"), gsi::arg ("dx"), gsi::arg ("dy"), + "@brief Create a new Matrix3d from the four coefficients of a Matrix2d plus a displacement\n" + ) + + gsi::constructor ("new", &new_matrix3d_m9, gsi::arg ("m11"), gsi::arg ("m12"), gsi::arg ("m13"), gsi::arg ("m21"), gsi::arg ("m22"), gsi::arg ("m23"), gsi::arg ("m31"), gsi::arg ("m32"), gsi::arg ("m33"), + "@brief Create a new Matrix3d from the nine matrix coefficients\n" + ) + + gsi::method_ext ("m", &coeff_m3, gsi::arg ("i"), gsi::arg ("j"), + "@brief Gets the m coefficient with the given index.\n" + "@return The coefficient [i,j]\n" + ) + + gsi::method ("to_s", &db::matrix_3d::to_string, + "@brief Convert the matrix to a string.\n" + "@return The string representing this matrix\n" + ) + + gsi::method ("inverted", &db::matrix_3d::inverted, + "@brief The inverse of this matrix.\n" + "@return The inverse of this matrix\n" + ) + + gsi::method_ext ("*", &prod_m3, gsi::arg ("m"), + "@brief Product of two matrices.\n" + "@param m The other matrix.\n" + "@return The matrix product self*m\n" + ) + + gsi::method_ext ("+", &sum_m3, gsi::arg ("m"), + "@brief Sum of two matrices.\n" + "@param m The other matrix.\n" + "@return The (element-wise) sum of self+m\n" + ) + + gsi::method_ext ("trans|*", &trans_p3, gsi::arg ("p"), + "@brief Transforms a point with this matrix.\n" + "@param p The point to transform.\n" + "@return The transformed point\n" + ) + + gsi::method_ext ("*", &trans_v3, gsi::arg ("v"), + "@brief Transforms a vector with this matrix.\n" + "@param v The vector to transform.\n" + "@return The transformed vector\n" + ) + + gsi::method_ext ("*", &trans_edge3, gsi::arg ("e"), + "@brief Transforms an edge with this matrix.\n" + "@param e The edge to transform.\n" + "@return The transformed edge\n" + ) + + gsi::method_ext ("*", &trans_box3, gsi::arg ("box"), + "@brief Transforms a box with this matrix.\n" + "@param box The box to transform.\n" + "@return The transformed box\n" + "\n" + "Please note that the box remains a box, even though the matrix supports shear and rotation. The returned box " + "will be the bounding box of the sheared and rotated rectangle." + ) + + gsi::method_ext ("*", &trans_simple_polygon3, gsi::arg ("p"), + "@brief Transforms a simple polygon with this matrix.\n" + "@param p The simple polygon to transform.\n" + "@return The transformed simple polygon\n" + ) + + gsi::method_ext ("*", &trans_polygon3, gsi::arg ("p"), + "@brief Transforms a polygon with this matrix.\n" + "@param p The polygon to transform.\n" + "@return The transformed polygon\n" + ) + + gsi::method_ext ("cplx_trans", &to_cplx_trans3, + "@brief Converts this matrix to a complex transformation (if possible).\n" + "@return The complex transformation.\n" + "This method is successful only if the matrix does not contain shear or perspective distortion components and the magnification must be isotropic.\n" + ) + + gsi::method ("mag_x", (double (db::matrix_3d::*) () const) &db::matrix_3d::mag_x, + "@brief Returns the x magnification of the magnification component of this matrix.\n" + "@return The magnification factor.\n" + ) + + gsi::method ("mag_y", (double (db::matrix_3d::*) () const) &db::matrix_3d::mag_y, + "@brief Returns the y magnification of the magnification component of this matrix.\n" + "@return The magnification factor.\n" + ) + + gsi::method ("angle", &db::matrix_3d::angle, + "@brief Returns the rotation angle of the rotation component of this matrix.\n" + "@return The angle in degree.\n" + "See the description of this class for details about the basic transformations." + ) + + gsi::method ("shear_angle", &db::matrix_3d::shear_angle, + "@brief Returns the magnitude of the shear component of this matrix.\n" + "@return The shear angle in degree.\n" + "The shear basic transformation will tilt the x axis towards the y axis and vice versa. The shear angle " + "gives the tilt angle of the axes towards the other one. The possible range for this angle is -45 to 45 degree." + "See the description of this class for details about the basic transformations." + ) + + gsi::method ("disp", (db::vector (db::matrix_3d::*) () const) &db::matrix_3d::disp, + "@brief Returns the displacement vector of this transformation.\n" + "\n" + "Starting with version 0.25 this method returns a vector type instead of a point.\n" + "@return The displacement vector.\n" + ) + + gsi::method ("tx", &db::matrix_3d::perspective_tilt_x, gsi::arg ("z"), + "@brief Returns the perspective tilt angle tx.\n" + "@param z The observer distance at which the tilt angle is computed.\n" + "@return The tilt angle tx.\n" + "The tx and ty parameters represent the perspective distortion. They denote a tilt of the xy plane around the y axis (tx) or the x axis (ty) in degree. " + "The same effect is achieved for different tilt angles at different observer distances. Hence, the observer distance must be specified at which the tilt angle is computed. " + "If the magnitude of the tilt angle is not important, z can be set to 1.\n" + ) + + gsi::method ("ty", &db::matrix_3d::perspective_tilt_y, gsi::arg ("z"), + "@brief Returns the perspective tilt angle ty.\n" + "@param z The observer distance at which the tilt angle is computed.\n" + "@return The tilt angle ty.\n" + "The tx and ty parameters represent the perspective distortion. They denote a tilt of the xy plane around the y axis (tx) or the x axis (ty) in degree. " + "The same effect is achieved for different tilt angles at different observer distances. Hence, the observer distance must be specified at which the tilt angle is computed. " + "If the magnitude of the tilt angle is not important, z can be set to 1.\n" + ) + + gsi::method ("is_mirror?", &db::matrix_3d::is_mirror, + "@brief Returns the mirror flag of this matrix.\n" + "@return True if this matrix has a mirror component.\n" + "See the description of this class for details about the basic transformations." + ); +} + + gsi::Class decl_Matrix3d ("db", "Matrix3d", - gsi::constructor ("new", &new_matrix3d, - "@brief Create a new Matrix3d representing a unit transformation" - ) + - gsi::constructor ("new", &new_matrix3d_m, gsi::arg ("m"), - "@brief Create a new Matrix3d representing a magnification\n" - "@param m The magnification\n" - ) + - gsi::constructor ("new", &new_matrix3d_t, gsi::arg ("t"), - "@brief Create a new Matrix3d from the given complex transformation" - "@param t The transformation from which to create the matrix\n" - ) + - gsi::constructor ("newc", &new_matrix3d_mrm, gsi::arg ("mag"), gsi::arg ("rotation"), gsi::arg ("mirrx"), - "@brief Create a new Matrix3d representing a isotropic magnification, rotation and mirroring\n" - "@param mag The magnification\n" - "@param rotation The rotation angle (in degree)\n" - "@param mirrx The mirror flag (at x axis)\n" - "\n" - "The order of execution of the operations is mirror, magnification and rotation.\n" - "This constructor is called 'newc' to distinguish it from the constructors taking coefficients ('c' is for composite).\n" - ) + - gsi::constructor ("newc", &new_matrix3d_smrm, gsi::arg ("shear"), gsi::arg ("mx"), gsi::arg ("my"), gsi::arg ("rotation"), gsi::arg ("mirrx"), - "@brief Create a new Matrix3d representing a shear, anisotropic magnification, rotation and mirroring\n" - "@param shear The shear angle\n" - "@param mx The magnification in x direction\n" - "@param mx The magnification in y direction\n" - "@param rotation The rotation angle (in degree)\n" - "@param mirrx The mirror flag (at x axis)\n" - "\n" - "The order of execution of the operations is mirror, magnification, rotation and shear.\n" - "This constructor is called 'newc' to distinguish it from the constructor taking the four matrix coefficients ('c' is for composite).\n" - ) + - gsi::constructor ("newc", &new_matrix3d_dsmrm, gsi::arg ("u"), gsi::arg ("shear"), gsi::arg ("mx"), gsi::arg ("my"), gsi::arg ("rotation"), gsi::arg ("mirrx"), - "@brief Create a new Matrix3d representing a displacement, shear, anisotropic magnification, rotation and mirroring\n" - "@param u The displacement\n" - "@param shear The shear angle\n" - "@param mx The magnification in x direction\n" - "@param mx The magnification in y direction\n" - "@param rotation The rotation angle (in degree)\n" - "@param mirrx The mirror flag (at x axis)\n" - "\n" - "The order of execution of the operations is mirror, magnification, rotation, shear and displacement.\n" - "This constructor is called 'newc' to distinguish it from the constructor taking the four matrix coefficients ('c' is for composite).\n" - "\n" - "Starting with version 0.25 the displacement is of vector type." - ) + - gsi::constructor ("newc", &new_matrix3d_pdsmrm, gsi::arg ("tx"), gsi::arg ("ty"), gsi::arg ("z"), gsi::arg ("u"), gsi::arg ("shear"), gsi::arg ("mx"), gsi::arg ("my"), gsi::arg ("rotation"), gsi::arg ("mirrx"), - "@brief Create a new Matrix3d representing a perspective distortion, displacement, shear, anisotropic magnification, rotation and mirroring\n" - "@param tx The perspective tilt angle x (around the y axis)\n" - "@param ty The perspective tilt angle y (around the x axis)\n" - "@param z The observer distance at which the tilt angles are given\n" - "@param u The displacement\n" - "@param shear The shear angle\n" - "@param mx The magnification in x direction\n" - "@param mx The magnification in y direction\n" - "@param rotation The rotation angle (in degree)\n" - "@param mirrx The mirror flag (at x axis)\n" - "\n" - "The order of execution of the operations is mirror, magnification, rotation, shear, perspective distortion and displacement.\n" - "This constructor is called 'newc' to distinguish it from the constructor taking the four matrix coefficients ('c' is for composite).\n" - "\n" - "The tx and ty parameters represent the perspective distortion. They denote a tilt of the xy plane around the y axis (tx) or the x axis (ty) in degree. " - "The same effect is achieved for different tilt angles for different observer distances. Hence, the observer distance must be given at which the tilt angles are given. " - "If the magnitude of the tilt angle is not important, z can be set to 1.\n" - "\n" - "Starting with version 0.25 the displacement is of vector type." - ) + - gsi::constructor ("new", &new_matrix3d_m4, gsi::arg ("m11"), gsi::arg ("m12"), gsi::arg ("m21"), gsi::arg ("m22"), - "@brief Create a new Matrix3d from the four coefficients of a Matrix2d\n" - ) + - gsi::constructor ("new", &new_matrix3d_m6, gsi::arg ("m11"), gsi::arg ("m12"), gsi::arg ("m21"), gsi::arg ("m22"), gsi::arg ("dx"), gsi::arg ("dy"), - "@brief Create a new Matrix3d from the four coefficients of a Matrix2d plus a displacement\n" - ) + - gsi::constructor ("new", &new_matrix3d_m9, gsi::arg ("m11"), gsi::arg ("m12"), gsi::arg ("m13"), gsi::arg ("m21"), gsi::arg ("m22"), gsi::arg ("m23"), gsi::arg ("m31"), gsi::arg ("m32"), gsi::arg ("m33"), - "@brief Create a new Matrix3d from the nine matrix coefficients\n" - ) + - gsi::method_ext ("m", &coeff_m3, gsi::arg ("i"), gsi::arg ("j"), - "@brief Gets the m coefficient with the given index.\n" - "@return The coefficient [i,j]\n" - ) + - gsi::method ("to_s", &db::Matrix3d::to_string, - "@brief Convert the matrix to a string.\n" - "@return The string representing this matrix\n" - ) + - gsi::method ("inverted", &db::Matrix3d::inverted, - "@brief The inverse of this matrix.\n" - "@return The inverse of this matrix\n" - ) + - gsi::method_ext ("trans", &trans_p3, gsi::arg ("p"), - "@brief Transforms a point with this matrix.\n" - "@param p The point to transform.\n" - "@return The product if self and the point p\n" - ) + - gsi::method_ext ("*", &prod_m3, gsi::arg ("m"), - "@brief Product of two matrices.\n" - "@param m The other matrix.\n" - "@return The matrix product self*m\n" - ) + - gsi::method_ext ("*", &trans_p3, gsi::arg ("p"), - "@brief Transform a point.\n" - "@param p The point to transform.\n" - "@return The transformed point\n" - ) + - gsi::method_ext ("+", &sum_m3, gsi::arg ("m"), - "@brief Sum of two matrices.\n" - "@param m The other matrix.\n" - "@return The (element-wise) sum of self+m\n" - ) + - gsi::method_ext ("cplx_trans", &to_cplx_trans3, - "@brief Converts this matrix to a complex transformation (if possible).\n" - "@return The complex transformation.\n" - "This method is successful only if the matrix does not contain shear or perspective distortion components and the magnification must be isotropic.\n" - ) + - gsi::method ("mag_x", (double (db::Matrix3d::*) () const) &db::Matrix3d::mag_x, - "@brief Returns the x magnification of the magnification component of this matrix.\n" - "@return The magnification factor.\n" - ) + - gsi::method ("mag_y", (double (db::Matrix3d::*) () const) &db::Matrix3d::mag_y, - "@brief Returns the y magnification of the magnification component of this matrix.\n" - "@return The magnification factor.\n" - ) + - gsi::method ("angle", &db::Matrix3d::angle, - "@brief Returns the rotation angle of the rotation component of this matrix.\n" - "@return The angle in degree.\n" - "See the description of this class for details about the basic transformations." - ) + - gsi::method ("shear_angle", &db::Matrix3d::shear_angle, - "@brief Returns the magnitude of the shear component of this matrix.\n" - "@return The shear angle in degree.\n" - "The shear basic transformation will tilt the x axis towards the y axis and vice versa. The shear angle " - "gives the tilt angle of the axes towards the other one. The possible range for this angle is -45 to 45 degree." - "See the description of this class for details about the basic transformations." - ) + - gsi::method ("disp", (db::DVector (db::Matrix3d::*) () const) &db::Matrix3d::disp, - "@brief Returns the displacement vector of this transformation.\n" - "\n" - "Starting with version 0.25 this method returns a vector type instead of a point.\n" - "@return The displacement vector.\n" - ) + - gsi::method ("tx", &db::Matrix3d::perspective_tilt_x, gsi::arg ("z"), - "@brief Returns the perspective tilt angle tx.\n" - "@param z The observer distance at which the tilt angle is computed.\n" - "@return The tilt angle tx.\n" - "The tx and ty parameters represent the perspective distortion. They denote a tilt of the xy plane around the y axis (tx) or the x axis (ty) in degree. " - "The same effect is achieved for different tilt angles at different observer distances. Hence, the observer distance must be specified at which the tilt angle is computed. " - "If the magnitude of the tilt angle is not important, z can be set to 1.\n" - ) + - gsi::method ("ty", &db::Matrix3d::perspective_tilt_y, gsi::arg ("z"), - "@brief Returns the perspective tilt angle ty.\n" - "@param z The observer distance at which the tilt angle is computed.\n" - "@return The tilt angle ty.\n" - "The tx and ty parameters represent the perspective distortion. They denote a tilt of the xy plane around the y axis (tx) or the x axis (ty) in degree. " - "The same effect is achieved for different tilt angles at different observer distances. Hence, the observer distance must be specified at which the tilt angle is computed. " - "If the magnitude of the tilt angle is not important, z can be set to 1.\n" - ) + - gsi::method ("is_mirror?", &db::Matrix3d::is_mirror, - "@brief Returns the mirror flag of this matrix.\n" - "@return True if this matrix has a mirror component.\n" - "See the description of this class for details about the basic transformations." - ) + + matrix3d_methods () + gsi::method_ext ("adjust", &adjust, gsi::arg ("landmarks_before"), gsi::arg ("landmarks_after"), gsi::arg ("flags"), gsi::arg ("fixed_point"), "@brief Adjust a 3d matrix to match the given set of landmarks\n" "\n" @@ -558,7 +731,7 @@ gsi::Class decl_Matrix3d ("db", "Matrix3d", ), "@brief A 3d matrix object used mainly for representing rotation, shear, displacement and perspective transformations.\n" "\n" - "This object represents a 3x3 matrix. This matrix is used to represent geometrical transformations " + "This object represents a 3x3 matrix. This matrix is used to implement generic geometrical transformations " "in the 2d space mainly. It can be decomposed into basic transformations: mirroring, rotation, shear, displacement and perspective distortion. " "In that case, the assumed execution order of the basic transformations is " "mirroring at the x axis, rotation, magnification, shear, displacement and perspective distortion." @@ -566,4 +739,16 @@ gsi::Class decl_Matrix3d ("db", "Matrix3d", "This class was introduced in version 0.22.\n" ); +gsi::Class decl_IMatrix3d ("db", "IMatrix3d", + matrix3d_methods (), + "@brief A 3d matrix object used mainly for representing rotation, shear, displacement and perspective transformations (integer coordinate version).\n" + "\n" + "This object represents a 3x3 matrix. This matrix is used to implement generic geometrical transformations " + "in the 2d space mainly. It can be decomposed into basic transformations: mirroring, rotation, shear, displacement and perspective distortion. " + "In that case, the assumed execution order of the basic transformations is " + "mirroring at the x axis, rotation, magnification, shear, displacement and perspective distortion." + "\n\n" + "The integer variant was introduced in version 0.27.\n" +); + } diff --git a/src/db/db/gsiDeclDbPolygon.cc b/src/db/db/gsiDeclDbPolygon.cc index c597112f5..fdd521591 100644 --- a/src/db/db/gsiDeclDbPolygon.cc +++ b/src/db/db/gsiDeclDbPolygon.cc @@ -1855,7 +1855,7 @@ Class decl_Polygon ("db", "Polygon", "\n" "This method was introduced in version 0.24.\n" ) + - method_ext ("transformed", &transformed_icplx_dp, gsi::arg ("t"), + method_ext ("#transformed", &transformed_icplx_dp, gsi::arg ("t"), "@brief Transforms the polygon with a complex transformation\n" "\n" "Transforms the polygon with the given complex transformation.\n" diff --git a/src/db/db/gsiDeclDbRecursiveShapeIterator.cc b/src/db/db/gsiDeclDbRecursiveShapeIterator.cc index 72070d1e7..091f3ba83 100644 --- a/src/db/db/gsiDeclDbRecursiveShapeIterator.cc +++ b/src/db/db/gsiDeclDbRecursiveShapeIterator.cc @@ -70,6 +70,27 @@ static db::DCplxTrans si_dtrans (const db::RecursiveShapeIterator *r) return db::CplxTrans (ly->dbu ()) * r->trans () * db::VCplxTrans (1.0 / ly->dbu ()); } +static db::DCplxTrans si_global_dtrans (const db::RecursiveShapeIterator *r) +{ + const db::Layout *ly = r->layout (); + tl_assert (ly != 0); + return db::CplxTrans (ly->dbu ()) * r->global_trans () * db::VCplxTrans (1.0 / ly->dbu ()); +} + +static db::DCplxTrans si_always_apply_dtrans (const db::RecursiveShapeIterator *r) +{ + const db::Layout *ly = r->layout (); + tl_assert (ly != 0); + return db::CplxTrans (ly->dbu ()) * r->always_apply () * db::VCplxTrans (1.0 / ly->dbu ()); +} + +static void si_set_global_dtrans (db::RecursiveShapeIterator *r, const db::DCplxTrans >) +{ + const db::Layout *ly = r->layout (); + tl_assert (ly != 0); + r->set_global_trans (db::VCplxTrans (1.0 / ly->dbu ()) * gt * db::CplxTrans (ly->dbu ())); +} + static void select_cells1 (db::RecursiveShapeIterator *r, const std::vector &cells) { std::set cc; @@ -270,7 +291,47 @@ Class decl_RecursiveShapeIterator ("db", "RecursiveS "\n" "This method has been introduced in version 0.23.\n" ) + - gsi::method ("region", &db::RecursiveShapeIterator::region, + gsi::method ("global_trans=", &db::RecursiveShapeIterator::set_global_trans, gsi::arg ("t"), + "@brief Sets the global transformation to apply to all shapes delivered\n" + "The global transformation will be applied to all shapes delivered by biasing the \"trans\" attribute.\n" + "The search regions apply to the coordinate space after global transformation.\n" + "\n" + "This method has been introduced in version 0.27.\n" + ) + + gsi::method ("global_trans", &db::RecursiveShapeIterator::global_trans, + "@brief Gets the global transformation to apply to all shapes delivered\n" + "See also \\global_trans=.\n" + "\n" + "This method has been introduced in version 0.27.\n" + ) + + gsi::method_ext ("global_dtrans=", &si_set_global_dtrans, + "@brief Sets the global transformation to apply to all shapes delivered (transformation in micrometer units)\n" + "The global transformation will be applied to all shapes delivered by biasing the \"trans\" attribute.\n" + "The search regions apply to the coordinate space after global transformation.\n" + "\n" + "This method has been introduced in version 0.27.\n" + ) + + gsi::method_ext ("global_dtrans", &si_global_dtrans, + "@brief Gets the global transformation to apply to all shapes delivered (in micrometer units)\n" + "See also \\global_dtrans=.\n" + "\n" + "This method has been introduced in version 0.27.\n" + ) + + gsi::method ("always_apply_trans", &db::RecursiveShapeIterator::always_apply, + "@brief Gets the global transformation if at top level, unity otherwise\n" + "As the global transformation is only applicable on top level, use this method to transform shapes and instances into their local (cell-level) version " + "while considering the global transformation properly.\n" + "\n" + "This method has been introduced in version 0.27.\n" + ) + + gsi::method_ext ("always_apply_dtrans", &si_always_apply_dtrans, + "@brief Gets the global transformation if at top level, unity otherwise (micrometer-unit version)\n" + "As the global transformation is only applicable on top level, use this method to transform shapes and instances into their local (cell-level) version " + "while considering the global transformation properly.\n" + "\n" + "This method has been introduced in version 0.27.\n" + ) + + gsi::method ("region", &db::RecursiveShapeIterator::region, "@brief Gets the basic region that is iterator is using\n" "The basic region is the overall box the region iterator iterates over. " "There may be an additional complex region that confines the region iterator. " diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index d1e7cdd87..898e01120 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -32,6 +32,7 @@ #include "dbShapes.h" #include "dbDeepShapeStore.h" #include "dbRegion.h" +#include "dbFillTool.h" #include "dbRegionProcessors.h" #include "dbCompoundOperation.h" #include "tlGlobPattern.h" @@ -713,6 +714,29 @@ tl::Variant complex_op (db::Region *region, db::CompoundRegionOperationNode *nod } } +static void +fill_region (const db::Region *fr, db::Cell *cell, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Point *origin, + db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box) +{ + db::fill_region (cell, *fr, fill_cell_index, fc_box, origin ? *origin : db::Point (), origin == 0, remaining_parts, fill_margin, remaining_polygons, glue_box); +} + +static void +fill_region_skew (const db::Region *fr, db::Cell *cell, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Vector &row_step, const db::Vector &column_step, const db::Point *origin, + db::Region *remaining_parts, const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Box &glue_box) +{ + db::fill_region (cell, *fr, fill_cell_index, fc_box, row_step, column_step, origin ? *origin : db::Point (), origin == 0, remaining_parts, fill_margin, remaining_polygons, glue_box); +} + +static void +fill_region_multi (const db::Region *fr, db::Cell *cell, db::cell_index_type fill_cell_index, const db::Box &fc_box, const db::Vector &row_step, const db::Vector &column_step, + const db::Vector &fill_margin, db::Region *remaining_polygons, const db::Point &origin, const db::Box &glue_box) +{ + db::fill_region_repeat (cell, *fr, fill_cell_index, fc_box, row_step, column_step, fill_margin, remaining_polygons, origin, glue_box); +} + +static db::Point default_origin; + // provided by gsiDeclDbPolygon.cc: int td_simple (); int po_any (); @@ -2320,8 +2344,32 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "@return The transformed region.\n" ) + + method ("transform", (db::Region &(db::Region::*)(const db::IMatrix2d &)) &db::Region::transform, gsi::arg ("t"), + "@brief Transform the region (modifies self)\n" + "\n" + "Transforms the region with the given 2d matrix transformation.\n" + "This version modifies the region and returns a reference to self.\n" + "\n" + "@param t The transformation to apply.\n" + "\n" + "@return The transformed region.\n" + "\n" + "This variant was introduced in version 0.27.\n" + ) + + method ("transform", (db::Region &(db::Region::*)(const db::IMatrix3d &)) &db::Region::transform, gsi::arg ("t"), + "@brief Transform the region (modifies self)\n" + "\n" + "Transforms the region with the given 3d matrix transformation.\n" + "This version modifies the region and returns a reference to self.\n" + "\n" + "@param t The transformation to apply.\n" + "\n" + "@return The transformed region.\n" + "\n" + "This variant was introduced in version 0.27.\n" + ) + method ("transformed", (db::Region (db::Region::*)(const db::Trans &) const) &db::Region::transformed, gsi::arg ("t"), - "@brief Transform the region\n" + "@brief Transforms the region\n" "\n" "Transforms the region with the given transformation.\n" "Does not modify the region but returns the transformed region.\n" @@ -2331,7 +2379,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "@return The transformed region.\n" ) + method ("transformed|#transformed_icplx", (db::Region (db::Region::*)(const db::ICplxTrans &) const) &db::Region::transformed, gsi::arg ("t"), - "@brief Transform the region with a complex transformation\n" + "@brief Transforms the region with a complex transformation\n" "\n" "Transforms the region with the given complex transformation.\n" "Does not modify the region but returns the transformed region.\n" @@ -2340,6 +2388,30 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "@return The transformed region.\n" ) + + method ("transformed", (db::Region (db::Region::*)(const db::IMatrix2d &) const) &db::Region::transformed, gsi::arg ("t"), + "@brief Transforms the region\n" + "\n" + "Transforms the region with the given 2d matrix transformation.\n" + "Does not modify the region but returns the transformed region.\n" + "\n" + "@param t The transformation to apply.\n" + "\n" + "@return The transformed region.\n" + "\n" + "This variant was introduced in version 0.27.\n" + ) + + method ("transformed", (db::Region (db::Region::*)(const db::IMatrix3d &) const) &db::Region::transformed, gsi::arg ("t"), + "@brief Transforms the region\n" + "\n" + "Transforms the region with the given 3d matrix transformation.\n" + "Does not modify the region but returns the transformed region.\n" + "\n" + "@param t The transformation to apply.\n" + "\n" + "@return The transformed region.\n" + "\n" + "This variant was introduced in version 0.27.\n" + ) + method_ext ("width_check", &width2, gsi::arg ("d"), gsi::arg ("whole_edges", false), gsi::arg ("metrics", db::metrics_type::Euclidian, "Euclidian"), gsi::arg ("ignore_angle", tl::Variant (), "default"), gsi::arg ("min_projection", tl::Variant (), "0"), gsi::arg ("max_projection", tl::Variant (), "max"), gsi::arg ("shielded", true), gsi::arg ("negative", false), "@brief Performs a width check with options\n" "@param d The minimum width for which the polygons are checked\n" @@ -2811,6 +2883,51 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "See \\base_verbosity= for details.\n" "\n" "This method has been introduced in version 0.26.\n" + ) + + gsi::method_ext ("fill", &fill_region, gsi::arg ("in_cell"), + gsi::arg ("fill_cell_index"), + gsi::arg ("fc_box"), + gsi::arg ("origin", &default_origin, "(0, 0)"), + gsi::arg ("remaining_parts", (db::Region *)0, "nil"), + gsi::arg ("fill_margin", db::Vector ()), + gsi::arg ("remaining_polygons", (db::Region *)0, "nil"), + gsi::arg ("glue_box", db::Box ()), + "@brief A mapping of \\Cell#fill_region to the Region class\n" + "\n" + "This method is equivalent to \\Cell#fill_region, but is based on Region (with the cell being the first parameter).\n" + "\n" + "This method has been introduced in version 0.27.\n" + ) + + gsi::method_ext ("fill", &fill_region_skew, gsi::arg ("in_cell"), + gsi::arg ("fill_cell_index"), + gsi::arg ("fc_origin"), + gsi::arg ("row_step"), + gsi::arg ("column_step"), + gsi::arg ("origin", &default_origin, "(0, 0)"), + gsi::arg ("remaining_parts", (db::Region *)0, "nil"), + gsi::arg ("fill_margin", db::Vector ()), + gsi::arg ("remaining_polygons", (db::Region *)0, "nil"), + gsi::arg ("glue_box", db::Box ()), + "@brief A mapping of \\Cell#fill_region to the Region class\n" + "\n" + "This method is equivalent to \\Cell#fill_region, but is based on Region (with the cell being the first parameter).\n" + "\n" + "This method has been introduced in version 0.27.\n" + ) + + gsi::method_ext ("fill_multi", &fill_region_multi, gsi::arg ("in_cell"), + gsi::arg ("fill_cell_index"), + gsi::arg ("fc_origin"), + gsi::arg ("row_step"), + gsi::arg ("column_step"), + gsi::arg ("fill_margin", db::Vector ()), + gsi::arg ("remaining_polygons", (db::Region *)0, "nil"), + gsi::arg ("origin", db::Point ()), + gsi::arg ("glue_box", db::Box ()), + "@brief A mapping of \\Cell#fill_region to the Region class\n" + "\n" + "This method is equivalent to \\Cell#fill_region, but is based on Region (with the cell being the first parameter).\n" + "\n" + "This method has been introduced in version 0.27.\n" ), "@brief A region (a potentially complex area consisting of multiple polygons)\n" "\n\n" diff --git a/src/db/unit_tests/dbFillToolTests.cc b/src/db/unit_tests/dbFillToolTests.cc new file mode 100644 index 000000000..b7857ccd5 --- /dev/null +++ b/src/db/unit_tests/dbFillToolTests.cc @@ -0,0 +1,315 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2021 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbFillTool.h" +#include "dbReader.h" +#include "dbRegion.h" +#include "dbTestSupport.h" +#include "dbRegion.h" +#include "tlUnitTest.h" +#include "tlStream.h" + +TEST(1) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/fill_tool1.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type fill_cell = ly.cell_by_name ("FILL_CELL").second; + db::cell_index_type top_cell = ly.cell_by_name ("TOP").second; + unsigned int fill_layer = ly.get_layer (db::LayerProperties (1, 0)); + + db::Region fill_region (db::RecursiveShapeIterator (ly, ly.cell (top_cell), fill_layer)); + + db::fill_region (&ly.cell (top_cell), fill_region, fill_cell, ly.cell (fill_cell).bbox (), db::Point (), false); + + CHECKPOINT(); + db::compare_layouts (_this, ly, tl::testsrc () + "/testdata/algo/fill_tool_au1.gds"); +} + +TEST(2) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/fill_tool2.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type fill_cell = ly.cell_by_name ("FILL_CELL").second; + db::cell_index_type top_cell = ly.cell_by_name ("TOP").second; + unsigned int fill_layer = ly.get_layer (db::LayerProperties (1, 0)); + + db::Region fill_region (db::RecursiveShapeIterator (ly, ly.cell (top_cell), fill_layer)); + + db::Region remaining_parts, remaining_polygons; + + db::fill_region (&ly.cell (top_cell), fill_region, fill_cell, ly.cell (fill_cell).bbox (), db::Point (), true, &remaining_parts, db::Vector (50, 100), &remaining_polygons); + + unsigned int l100 = ly.insert_layer (db::LayerProperties (100, 0)); + unsigned int l101 = ly.insert_layer (db::LayerProperties (101, 0)); + remaining_parts.insert_into (&ly, top_cell, l100); + remaining_polygons.insert_into (&ly, top_cell, l101); + + CHECKPOINT(); + db::compare_layouts (_this, ly, tl::testsrc () + "/testdata/algo/fill_tool_au2.gds"); +} + +TEST(3) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/fill_tool3.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type fill_cell = ly.cell_by_name ("FILL_CELL").second; + db::cell_index_type top_cell = ly.cell_by_name ("TOP").second; + unsigned int fill_layer = ly.get_layer (db::LayerProperties (1, 0)); + + db::Region fill_region (db::RecursiveShapeIterator (ly, ly.cell (top_cell), fill_layer)); + + db::Region remaining_parts, remaining_polygons; + + db::Vector ko (-100, -130); + db::Vector rs (230, 40); + db::Vector cs (40, 230); + db::Box fc_box (db::Point () + ko, db::Point (rs.x (), cs.y ()) + ko); + db::fill_region (&ly.cell (top_cell), fill_region, fill_cell, fc_box, rs, cs, db::Point (), true, &remaining_parts, db::Vector (50, 100), &remaining_polygons); + + unsigned int l100 = ly.insert_layer (db::LayerProperties (100, 0)); + unsigned int l101 = ly.insert_layer (db::LayerProperties (101, 0)); + remaining_parts.insert_into (&ly, top_cell, l100); + remaining_polygons.insert_into (&ly, top_cell, l101); + + CHECKPOINT(); + db::compare_layouts (_this, ly, tl::testsrc () + "/testdata/algo/fill_tool_au3.gds"); +} + +TEST(3a) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/fill_tool3.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type fill_cell = ly.cell_by_name ("FILL_CELL").second; + db::cell_index_type top_cell = ly.cell_by_name ("TOP").second; + unsigned int fill_layer = ly.get_layer (db::LayerProperties (1, 0)); + + db::Region fill_region (db::RecursiveShapeIterator (ly, ly.cell (top_cell), fill_layer)); + + db::Region remaining_parts, remaining_polygons; + + db::Vector ko (-100, -130); + db::Vector rs (230, 40); + db::Vector cs (-40, 230); + db::Box fc_box (db::Point () + ko, db::Point (rs.x (), cs.y ()) + ko); + db::fill_region (&ly.cell (top_cell), fill_region, fill_cell, fc_box, rs, cs, db::Point (), true, &remaining_parts, db::Vector (50, 100), &remaining_polygons); + + unsigned int l100 = ly.insert_layer (db::LayerProperties (100, 0)); + unsigned int l101 = ly.insert_layer (db::LayerProperties (101, 0)); + remaining_parts.insert_into (&ly, top_cell, l100); + remaining_polygons.insert_into (&ly, top_cell, l101); + + CHECKPOINT(); + db::compare_layouts (_this, ly, tl::testsrc () + "/testdata/algo/fill_tool_au3a.gds"); +} + +TEST(3b) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/fill_tool3.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type fill_cell = ly.cell_by_name ("FILL_CELL").second; + db::cell_index_type top_cell = ly.cell_by_name ("TOP").second; + unsigned int fill_layer = ly.get_layer (db::LayerProperties (1, 0)); + + db::Region fill_region (db::RecursiveShapeIterator (ly, ly.cell (top_cell), fill_layer)); + + db::Region remaining_parts, remaining_polygons; + + db::Vector ko (-100, -130); + db::Vector rs (230, -40); + db::Vector cs (40, 230); + db::Box fc_box (db::Point () + ko, db::Point (rs.x (), cs.y ()) + ko); + db::fill_region (&ly.cell (top_cell), fill_region, fill_cell, fc_box, rs, cs, db::Point (), true, &remaining_parts, db::Vector (50, 100), &remaining_polygons); + + unsigned int l100 = ly.insert_layer (db::LayerProperties (100, 0)); + unsigned int l101 = ly.insert_layer (db::LayerProperties (101, 0)); + remaining_parts.insert_into (&ly, top_cell, l100); + remaining_polygons.insert_into (&ly, top_cell, l101); + + CHECKPOINT(); + db::compare_layouts (_this, ly, tl::testsrc () + "/testdata/algo/fill_tool_au3b.gds"); +} + +TEST(3c) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/fill_tool3.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type fill_cell = ly.cell_by_name ("FILL_CELL").second; + db::cell_index_type top_cell = ly.cell_by_name ("TOP").second; + unsigned int fill_layer = ly.get_layer (db::LayerProperties (1, 0)); + + db::Region fill_region (db::RecursiveShapeIterator (ly, ly.cell (top_cell), fill_layer)); + + db::Region remaining_parts, remaining_polygons; + + db::Vector ko (-100, -130); + db::Vector rs (230, -40); + db::Vector cs (-40, 230); + db::Box fc_box (db::Point () + ko, db::Point (rs.x (), cs.y ()) + ko); + db::fill_region (&ly.cell (top_cell), fill_region, fill_cell, fc_box, rs, cs, db::Point (), true, &remaining_parts, db::Vector (50, 100), &remaining_polygons); + + unsigned int l100 = ly.insert_layer (db::LayerProperties (100, 0)); + unsigned int l101 = ly.insert_layer (db::LayerProperties (101, 0)); + remaining_parts.insert_into (&ly, top_cell, l100); + remaining_polygons.insert_into (&ly, top_cell, l101); + + CHECKPOINT(); + db::compare_layouts (_this, ly, tl::testsrc () + "/testdata/algo/fill_tool_au3c.gds"); +} + +TEST(4) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/fill_tool4.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type fill_cell = ly.cell_by_name ("FILL_CELL").second; + db::cell_index_type top_cell = ly.cell_by_name ("TOP").second; + unsigned int fill_layer = ly.get_layer (db::LayerProperties (1, 0)); + + db::Region fill_region (db::RecursiveShapeIterator (ly, ly.cell (top_cell), fill_layer)); + + db::Region remaining_polygons; + + db::Vector ko (-100, -130); + db::Vector rs (230, 0); + db::Vector cs (0, 230); + db::Box fc_box (db::Point () + ko, db::Point (rs.x (), cs.y ()) + ko); + db::fill_region_repeat (&ly.cell (top_cell), fill_region, fill_cell, fc_box, rs, cs, db::Vector (50, 100), &remaining_polygons); + + unsigned int l101 = ly.insert_layer (db::LayerProperties (101, 0)); + remaining_polygons.insert_into (&ly, top_cell, l101); + + CHECKPOINT(); + db::compare_layouts (_this, ly, tl::testsrc () + "/testdata/algo/fill_tool_au4.gds"); +} + +TEST(4b) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/fill_tool4.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type fill_cell = ly.cell_by_name ("FILL_CELL").second; + db::cell_index_type top_cell = ly.cell_by_name ("TOP").second; + unsigned int fill_layer = ly.get_layer (db::LayerProperties (1, 0)); + + db::Region fill_region (db::RecursiveShapeIterator (ly, ly.cell (top_cell), fill_layer)); + + db::Region remaining_polygons; + + db::Vector ko (-100, -130); + db::Vector rs (230, 0); + db::Vector cs (0, 230); + db::Box fc_box (db::Point () + ko, db::Point ()); + db::fill_region (&ly.cell (top_cell), fill_region, fill_cell, fc_box, rs, cs, db::Point (), true, &remaining_polygons); + + unsigned int l101 = ly.insert_layer (db::LayerProperties (101, 0)); + remaining_polygons.insert_into (&ly, top_cell, l101); + + CHECKPOINT(); + db::compare_layouts (_this, ly, tl::testsrc () + "/testdata/algo/fill_tool_au4b.gds"); +} + +TEST(4c) +{ + db::Layout ly; + { + std::string fn (tl::testsrc ()); + fn += "/testdata/algo/fill_tool4.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::cell_index_type fill_cell = ly.cell_by_name ("FILL_CELL").second; + db::cell_index_type top_cell = ly.cell_by_name ("TOP").second; + unsigned int fill_layer = ly.get_layer (db::LayerProperties (1, 0)); + + db::Region fill_region (db::RecursiveShapeIterator (ly, ly.cell (top_cell), fill_layer)); + + db::Region remaining_polygons; + + db::Vector ko (-100, -130); + db::Vector rs (230, 0); + db::Vector cs (0, 230); + db::Box fc_box (db::Point () + ko, db::Point (rs.x (), cs.y ()) + ko); + db::fill_region (&ly.cell (top_cell), fill_region, fill_cell, fc_box.enlarged (db::Vector (100, 100)), rs, cs, db::Point (), true, &remaining_polygons); + + unsigned int l101 = ly.insert_layer (db::LayerProperties (101, 0)); + remaining_polygons.insert_into (&ly, top_cell, l101); + + CHECKPOINT(); + db::compare_layouts (_this, ly, tl::testsrc () + "/testdata/algo/fill_tool_au4c.gds"); +} diff --git a/src/db/unit_tests/dbMatrixTests.cc b/src/db/unit_tests/dbMatrixTests.cc index cfaf0ac70..605b41f04 100644 --- a/src/db/unit_tests/dbMatrixTests.cc +++ b/src/db/unit_tests/dbMatrixTests.cc @@ -630,3 +630,12 @@ TEST(10) EXPECT_EQ ((m * p[2]).to_string (), "2,3"); } +TEST(11) +{ + // double and integer versions basic functionality + EXPECT_EQ ((db::Matrix2d (1.0, 0.5, -0.5, 2.0) * db::DPoint (1, 2)).to_string (), "2,3.5"); + EXPECT_EQ ((db::IMatrix2d (1.0, 0.5, -0.5, 2.0) * db::Point (10, 20)).to_string (), "20,35"); + EXPECT_EQ ((db::Matrix3d (1.0, 0.5, 0.0, -0.5, 2.0, 1.0, 0.0, 0.0, 1.0) * db::DPoint (1, 2)).to_string (), "2,4.5"); + EXPECT_EQ ((db::IMatrix3d (1.0, 0.5, 0.0, -0.5, 2.0, 1.0, 0.0, 0.0, 1.0) * db::DPoint (10, 20)).to_string (), "20,36"); +} + diff --git a/src/db/unit_tests/dbPolygonToolsTests.cc b/src/db/unit_tests/dbPolygonToolsTests.cc index 1764fd4e7..0d55b1ae1 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), diff --git a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc index cf559bb1f..cd9829ce7 100644 --- a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc +++ b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc @@ -107,12 +107,35 @@ TEST(1) std::string x; + db::RecursiveShapeIterator i0s (g, c0, 0); + x = collect(i0s, g); + EXPECT_EQ (x, "[$1](0,100;1000,1200)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); + EXPECT_EQ (i0s.bbox ().to_string (), "(-1200,0;2200,1200)"); + + i0s.set_global_trans (db::ICplxTrans (2.0)); + x = collect(i0s, g); + EXPECT_EQ (x, "[$1](0,200;2000,2400)/[$2](0,200;2000,2400)/[$3](200,0;2200,2200)/[$4](2400,0;4400,2200)/[$4](-2400,0;-200,2000)"); + EXPECT_EQ (i0s.bbox ().to_string (), "(-2400,0;4400,2400)"); + db::RecursiveShapeIterator i1 (g, c0, 0, db::Box (0, 0, 100, 100)); x = collect(i1, g); EXPECT_EQ (x, "[$1](0,100;1000,1200)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)"); x = collect_with_copy(i1, g); EXPECT_EQ (x, "[$1](0,100;1000,1200)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)"); + i1.set_global_trans (db::ICplxTrans (db::Trans (db::Vector (10, 20)))); + i1.set_region (db::Box (10, 20, 110, 120)); + x = collect(i1, g); + EXPECT_EQ (x, "[$1](10,120;1010,1220)/[$2](10,120;1010,1220)/[$3](110,20;1110,1120)"); + x = collect_with_copy(i1, g); + EXPECT_EQ (x, "[$1](10,120;1010,1220)/[$2](10,120;1010,1220)/[$3](110,20;1110,1120)"); + + i1.reset (); + x = collect(i1, g); + EXPECT_EQ (x, "[$1](10,120;1010,1220)/[$2](10,120;1010,1220)/[$3](110,20;1110,1120)"); + x = collect_with_copy(i1, g); + EXPECT_EQ (x, "[$1](10,120;1010,1220)/[$2](10,120;1010,1220)/[$3](110,20;1110,1120)"); + db::RecursiveShapeIterator i1_1inf (g, c0, 0, db::Box (0, 0, 100, 100)); i1_1inf.min_depth(1); x = collect(i1_1inf, g); @@ -731,7 +754,7 @@ namespace { public: FlatPusher (std::set *boxes) : mp_boxes (boxes) { } - void shape (const db::RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) + void shape (const db::RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans & /*always_apply*/, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) { mp_boxes->insert (trans * shape.bbox ()); } @@ -766,6 +789,8 @@ TEST(4) } + // ... + db::Box search_box (2500, 2500, 7500, 7500); std::set selected_boxes; @@ -794,6 +819,45 @@ TEST(4) EXPECT_EQ (selected_boxes.size () > 100, true); EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); + // with global trans + + selected_boxes.clear (); + selected_boxes2.clear (); + db::ICplxTrans ctr (db::Trans (db::Vector (10, 20))); + + { + db::RecursiveShapeIterator iter (g, c0, 0, search_box, true); + iter.set_global_trans (ctr); + for ( ; !iter.at_end (); ++iter) { + selected_boxes.insert (iter->bbox ().transformed (iter.trans ())); + } + } + + { + for (std::set::const_iterator b = boxes.begin (); b != boxes.end (); ++b) { + if (search_box.overlaps (b->transformed (ctr))) { + selected_boxes2.insert (b->transformed (ctr)); + } + } + } + + EXPECT_EQ (selected_boxes.size () > 100, true); + EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); + + // push mode + { + selected_boxes.clear (); + FlatPusher pusher (&selected_boxes); + db::RecursiveShapeIterator iter (g, c0, 0, search_box, true); + iter.set_global_trans (ctr); + iter.push (&pusher); + } + + EXPECT_EQ (selected_boxes.size () > 100, true); + EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); + + // ... + db::Box search_box2 (500, 500, 1000, 1000); selected_boxes.clear (); @@ -882,6 +946,40 @@ TEST(5) EXPECT_EQ (selected_boxes.size () > 100, true); EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); + selected_boxes.clear (); + selected_boxes2.clear (); + + db::ICplxTrans ctr (db::Trans (db::Vector (10, 20))); + + { + db::RecursiveShapeIterator iter = db::RecursiveShapeIterator (g, c0, 0, search_box, true); + iter.set_global_trans (ctr); + for ( ; !iter.at_end (); ++iter) { + selected_boxes.insert (iter.trans () * iter->bbox ()); + } + } + + for (std::set::const_iterator b = boxes.begin (); b != boxes.end (); ++b) { + if (search_box.overlaps (b->transformed (ctr))) { + selected_boxes2.insert (b->transformed (ctr)); + } + } + + EXPECT_EQ (selected_boxes.size () > 100, true); + EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); + + // push mode + { + selected_boxes.clear (); + FlatPusher pusher (&selected_boxes); + db::RecursiveShapeIterator iter (g, c0, 0, search_box, true); + iter.set_global_trans (ctr); + iter.push (&pusher); + } + + EXPECT_EQ (selected_boxes.size () > 100, true); + EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); + db::Box search_box2 (500, 500, 1000, 1000); selected_boxes.clear (); @@ -936,7 +1034,7 @@ public: m_text += std::string ("leave_cell(") + iter->layout ()->cell_name (cell->cell_index ()) + ")\n"; } - virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) + virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) { m_text += std::string ("new_inst(") + iter->layout ()->cell_name (inst.object ().cell_index ()); if (all) { @@ -946,9 +1044,9 @@ public: return NI_all; } - virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) + virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) { - m_text += std::string ("new_inst_member(") + iter->layout ()->cell_name (inst.object ().cell_index ()) + "," + tl::to_string (trans); + m_text += std::string ("new_inst_member(") + iter->layout ()->cell_name (inst.object ().cell_index ()) + "," + tl::to_string (always_apply * trans); if (all) { m_text += ",all"; } @@ -956,7 +1054,7 @@ public: return true; } - virtual void shape (const db::RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) + virtual void shape (const db::RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans & /*always_apply*/, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) { m_text += "shape(" + shape.to_string () + "," + tl::to_string (trans) + ")\n"; } @@ -971,9 +1069,9 @@ class ReceiverRejectingACellInstanceArray public: ReceiverRejectingACellInstanceArray (db::cell_index_type rejected) : m_rejected (rejected) { } - virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box ®ion, const box_tree_type *complex_region, bool all) + virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box ®ion, const box_tree_type *complex_region, bool all) { - LoggingReceiver::new_inst (iter, inst, region, complex_region, all); + LoggingReceiver::new_inst (iter, inst, always_apply, region, complex_region, all); return inst.object ().cell_index () != m_rejected ? NI_all : NI_skip; } @@ -987,9 +1085,9 @@ class ReceiverRejectingACellInstanceArrayExceptOne public: ReceiverRejectingACellInstanceArrayExceptOne (db::cell_index_type rejected) : m_rejected (rejected) { } - virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box ®ion, const box_tree_type *complex_region, bool all) + virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box ®ion, const box_tree_type *complex_region, bool all) { - LoggingReceiver::new_inst (iter, inst, region, complex_region, all); + LoggingReceiver::new_inst (iter, inst, always_apply, region, complex_region, all); return inst.object ().cell_index () != m_rejected ? NI_all : NI_single; } @@ -1003,9 +1101,9 @@ class ReceiverRejectingACellInstance public: ReceiverRejectingACellInstance (db::cell_index_type rejected, const db::ICplxTrans &trans_rejected) : m_rejected (rejected), m_trans_rejected (trans_rejected) { } - virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all) + virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all) { - LoggingReceiver::new_inst_member (iter, inst, trans, region, complex_region, all); + LoggingReceiver::new_inst_member (iter, inst, always_apply, trans, region, complex_region, all); return inst.object ().cell_index () != m_rejected || trans != m_trans_rejected; } @@ -1127,6 +1225,100 @@ TEST(10) "end\n" ); + LoggingReceiver lr1_gt; + db::RecursiveShapeIterator i1_gt (g, c0, 0); + i1_gt.set_global_trans (db::ICplxTrans (db::Trans (db::Vector (10, 20)))); + i1_gt.push (&lr1_gt); + + EXPECT_EQ (lr1_gt.text (), + "begin\n" + "new_inst($2,all)\n" + "new_inst_member($2,r0 *1 10,20,all)\n" + // It's a bit weird to have shape events after new_inst_member, but remember, new_inst_member is a query callback, not an event. + "shape(box (0,0;1000,1000),r0 *1 10,20)\n" + "shape(box (-1000,0;0,1000),r0 *1 10,20)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 10,20)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 0,2000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 10,2020)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3010,1020)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3010,3020)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 10,6020,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 10,6020)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 0,2000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 10,8020)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3010,7020)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3010,9020)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6010,20,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6010,20)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 0,2000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6010,2020)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 9010,1020)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 9010,3020)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6010,6020,all)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6010,6020)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 0,2000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6010,8020)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 9010,7020)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000,all)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 9010,9020)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "end\n" + ); + ReceiverRejectingACellInstanceArray rr1 (c2.cell_index ()); db::RecursiveShapeIterator ir1 (g, c0, 0); ir1.push (&rr1); diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 3af34f474..1fc2fbe1d 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -8,6 +8,7 @@ include($$PWD/../../lib_ut.pri) SOURCES = \ dbCompoundOperationTests.cc \ + dbFillToolTests.cc \ dbRecursiveInstanceIteratorTests.cc \ dbRegionUtilsTests.cc \ dbUtilsTests.cc \ diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index d5d419e76..d7cd228ed 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -63,6 +63,36 @@ module DRC @in_context = nil end + + def shift(x, y) + self._context("shift") do + RBA::DCplxTrans::new(RBA::DVector::new(_make_value(x) * self.dbu, _make_value(y) * self.dbu)) + end + end + + def magnify(m) + self._context("magnify") do + RBA::DCplxTrans::new(_make_numeric_value(m)) + end + end + + def rotate(a) + self._context("rotate") do + RBA::DCplxTrans::new(1.0, _make_numeric_value(a), false, RBA::DVector::new) + end + end + + def mirror_x + self._context("mirror_x") do + RBA::DCplxTrans::new(1.0, 0.0, true, RBA::DVector::new) + end + end + + def mirror_y + self._context("mirror_y") do + RBA::DCplxTrans::new(1.0, 180.0, true, RBA::DVector::new) + end + end def joined DRCJoinFlag::new(true) @@ -212,6 +242,26 @@ module DRC DRCAreaAndPerimeter::new(r, 1.0, f) end end + + def fill_pattern(name) + DRCFillCell::new(name) + end + + def hstep(x, y = nil) + DRCFillStep::new(true, x, y) + end + + def vstep(x, y = nil) + DRCFillStep::new(false, x, y) + end + + def auto_origin + DRCFillOrigin::new + end + + def origin(x, y) + DRCFillOrigin::new(x, y) + end def tile_size(x, y = nil) DRCTileSize::new(_make_value(x) * self.dbu, _make_value(y || x) * self.dbu) @@ -1494,6 +1544,22 @@ CODE nil end + # %DRC% + # @name global_transform + # @brief Gets or sets a global transformation + # @synopsis global_transform + # @synopsis global_transform([ transformations ]) + # + # Applies a global transformation to the default source layout. + # See \Source#global_transform for a description of this feature. + + def global_transform(*args) + self._context("global_transform") do + @def_source = layout.global_transform(*args) + end + nil + end + # %DRC% # @name cheat # @brief Hierarchy cheats @@ -1679,8 +1745,6 @@ CODE pull_overlapping rectangles rectilinear - rotate - rotated rounded_corners scale scaled @@ -2107,6 +2171,47 @@ CODE obj.send(method, *args) end end + + def _bx + @bx + end + + def _by + @by + end + + def _tx + @tx + end + + def _ty + @ty + end + + def _output_layout + if @output_layout + output = @output_layout + else + output = @def_layout + output || raise("No output layout specified") + end + output + end + + def _output_cell + if @output_layout + if @output_cell + output_cell = @output_cell + elsif @def_cell + output_cell = @output_layout.cell(@def_cell.name) || @output_layout.create_cell(@def_cell.name) + end + output_cell || raise("No output cell specified (see 'target' instruction)") + else + output_cell = @output_cell || @def_cell + output_cell || raise("No output cell specified") + end + output_cell + end def _start(job_description) @@ -2439,6 +2544,13 @@ CODE v end + def _use_output_layer(li) + if !@used_output_layers[li] + @output_layers.push(li) + @used_output_layers[li] = true + end + end + private def _make_string(v) @@ -2449,7 +2561,7 @@ CODE end end - def _input(layout, cell_index, layers, sel, box, clip, overlapping, shape_flags, cls) + def _input(layout, cell_index, layers, sel, box, clip, overlapping, shape_flags, global_trans, cls) if layers.empty? && ! @deep @@ -2463,6 +2575,7 @@ CODE iter = RBA::RecursiveShapeIterator::new(layout, layout.cell(cell_index), layers) end iter.shape_flags = shape_flags + iter.global_dtrans = global_trans sel.each do |s| if s == "-" @@ -2500,8 +2613,12 @@ CODE end # clip if a box is specified - if box && clip && (cls == RBA::Region || cls == RBA::Edge) - r &= RBA::Region::new(box) + # TODO: the whole clip thing could be a part of the Region constructor + if cls == RBA::Region && clip && box + # HACK: deep regions will always clip in the constructor, so skip this + if ! @deep + r &= RBA::Region::new(box) + end end end @@ -2529,20 +2646,8 @@ CODE else - if @output_layout - output = @output_layout - if @output_cell - output_cell = @output_cell - elsif @def_cell - output_cell = @output_layout.cell(@def_cell.name) || @output_layout.create_cell(@def_cell.name) - end - output_cell || raise("No output cell specified (see 'target' instruction)") - else - output = @def_layout - output || raise("No output layout specified") - output_cell = @output_cell || @def_cell - output_cell || raise("No output cell specified") - end + output = self._output_layout + output_cell = self._output_cell info = nil if args.size == 1 @@ -2606,7 +2711,7 @@ CODE data end - + def make_source(layout, cell = nil, path = nil) name = "layout" + @lnum.to_s @lnum += 1 diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index f13b8b9c6..489f2da3a 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -4231,6 +4231,262 @@ CODE @engine._vcmd(@engine, :_output, self.data, *args) end end + + # %DRC% + # @name fill + # @brief Fills the region with regular pattern of shapes + # @synopsis layer.fill([ options ]) + # + # This method will attempt to fill the polygons of the layer with a regular pattern + # of shapes. + # + # The fill function currently is not available in deep mode. + # + # Options are: + # @ul + # @li @b hstep(x) @/b or @b hstep(x, y) @/b: specifies the horizontal step pitch of the pattern. x must be + # a positive value. A vertical displacement component can be specified too, which results in a skewed pattern. @/li + # @li @b vstep(y) @/b or @b vstep(x, y) @/b: specifies the vertical step pitch of the pattern. y must be + # a positive value. A horizontal displacement component can be specified too, which results in a skewed pattern. @/li + # @li @b origin(x, y) @/b: specifies a fixed point to align the pattern with. This point specifies the location + # of the reference point for one pattern cell. @/li + # @li @b auto_origin @/b: lets the algorithm choose the origin. This may result is a slightly better fill coverage + # as the algorithm is able to determine a pattern origin per fill island. @/li + # @li @b fill_pattern(..) @/b: specifies the fill pattern. @/li + # @/ul + # + # "fill_pattern" generates a fill pattern object. This object is used for configuring the fill pattern + # content. Fill pattern need to be named. The name will be used for generating the fill cell. + # + # To provide a fill pattern, create a fill pattern object and add shapes to it. The following example creates + # a fill pattern named "FILL_CELL" and adds a 1x1 micron box on layer 1/0: + # + # @code + # p = fill_pattern("FILL_CELL") + # p.shape(1, 0, box(0.0, 0.0, 1.0, 1.0)) + # @/code + # + # See \global#box for details about the box specification. You can also add paths or polygons with \global#path or \global#polygon. + # + # A more compact way of writing this is: + # + # @code + # p = fill_pattern("FILL_CELL").shape(1, 0, box(0.0, 0.0, 1.0, 1.0)) + # @/code + # + # The fill pattern can be given a reference point which is used for placing the pattern. The reference point + # is the one which is aligned with the pattern origin. The following code will assign (-0.5, -0.5) as the reference + # point for the 1x1 micron rectangle. Hence the reference point is a little below and left of the rectangle which + # in turn shifts the rectangle fill pattern to the right and up: + # + # @code + # p = fill_pattern("FILL_CELL") + # p.shape(1, 0, box(0.0, 0.0, 1.0, 1.0)) + # p.origin(-0.5, -0.5) + # @/code + # + # Without a reference point given, the lower left corner of the fill pattern's bounding box will be used + # as the reference point. The reference point will also defined the footprint of the fill cell - more precisely + # the lower left corner. When step vectors are given, the fill cell's footprint is taken to be a rectangle + # having the horizontal and vertical step pitch for width and height respectively. This way the fill cells + # will be arrange seamlessly. However, the cell's dimensions can be changed, so that the fill cells + # can overlap or there is a space between the cells. To change the dimensions use the "dim" method. + # + # The following example specifies a fill cell with an active area of -0.5 .. 1.5 in both directions + # (2 micron width and height). With these dimensions the fill cell's footprint is independent of the + # step pitch: + # + # @code + # p = fill_pattern("FILL_CELL") + # p.shape(1, 0, box(0.0, 0.0, 1.0, 1.0)) + # p.origin(-0.5, -0.5) + # p.dim(2.0, 2.0) + # @/code + # + # With these ingredients will can use the fill function. The first example fills the polygons + # of "to_fill" with an orthogonal pattern of 1x1 micron rectangles with a pitch of 2 microns: + # + # @code + # pattern = fill_pattern("FILL_CELL").shape(1, 0, box(0.0, 0.0, 1.0, 1.0)).origin(-0.5, -0.5) + # to_fill.fill(pattern, hstep(2.0), vstep(2.0)) + # @/code + # + # This second example will create a skewed fill pattern in auto-origin mode: + # + # @code + # pattern = fill_pattern("FILL_CELL").shape(1, 0, box(0.0, 0.0, 1.0, 1.0)).origin(-0.5, -0.5) + # to_fill.fill(pattern, hstep(2.0, 1.0), vstep(-1.0, 2.0), auto_origin) + # @/code + # + # The fill function can only work with a target layout for output. + # It will not work for report output. + # + # The layers generated by the fill cells is only available for input later in the + # script if the output layout is identical to the input layouts. + # If you need the area missed by the fill function, try \fill_with_left. + + def fill(*args) + self._fill(false, *args) + end + + # %DRC% + # @name fill_with_left + # @brief Fills the region with regular pattern of shapes + # @synopsis layer.fill_with_left([ options ]) + # + # This method has the same call syntax and functionality than \fill. Other than this method + # it will return the area not covered by fill cells as a DRC layer. + + def fill_with_left(*args) + self._fill(true, *args) + end + + def _fill(with_left, *args) + + m = with_left ? "fill_with_left" : "fill" + + # generation of new cells not tested in deep mode + @deep && raise("#{m} command not supported in deep mode currently") + + (@engine._output_layout && @engine._output_cell) || raise("#{m} command needs an output layout and output cell") + + source = @engine.source + row_step = nil + column_step = nil + pattern = nil + origin = RBA::DPoint::new + + args.each_with_index do |a,ai| + if a.is_a?(DRCSource) + if source + raise("Duplicate source specification for '#{m}' at argument ##{ai+1}") + end + source = a + elsif a.is_a?(DRCFillCell) + if pattern + raise("Duplicate fill pattern specification for '#{m}' at argument ##{ai+1}") + end + pattern = a + elsif a.is_a?(DRCFillStep) + if a.for_row + if row_step + raise("Duplicate hstep specification for '#{m}' at argument ##{ai+1}") + end + row_step = a.step + else + if column_step + raise("Duplicate vstep specification for '#{m}' at argument ##{ai+1}") + end + column_step = a.step + end + elsif a.is_a?(DRCFillOrigin) + origin = a.origin + else + raise("Argument ##{ai+1} not understood for '#{m}'") + end + end + + if !pattern + raise("No fill pattern given for '#{m}' (use 'fill_pattern')") + end + + if !row_step + row_step = RBA::DVector::new(pattern.default_xpitch, 0) + end + if !column_step + column_step = RBA::DVector::new(0, pattern.default_ypitch) + end + + dbu_trans = RBA::VCplxTrans::new(1.0 / @engine.dbu) + + result = nil + + fill_cell = pattern.create_cell(@engine._output_layout, @engine) + top_cell = @engine._output_cell + fc_box = dbu_trans * pattern.cell_box(row_step.x, column_step.y) + rs = dbu_trans * row_step + cs = dbu_trans * column_step + origin = origin ? dbu_trans * origin : nil + fc_index = fill_cell.cell_index + + if @engine._tx && @engine._ty + + tp = RBA::TilingProcessor::new + tp.dbu = @engine.dbu + tp.frame = RBA::CplxTrans::new(@engine.dbu) * self.data.bbox + tp.scale_to_dbu = false + tp.tile_size(@engine._tx, @engine._ty) + bx = [ @engine._bx || 0.0, row_step.x ].max + by = [ @engine._by || 0.0, column_step.y ].max + tp.tile_border(bx, by) + tp.threads = (@engine.threads || 1) + + result_arg = "nil" + if with_left + result = RBA::Region::new + result_arg = "result" + tp.output(result_arg, result) + end + + tp.input("region", self.data) + tp.var("top_cell", top_cell) + tp.var("fc_box", fc_box) + tp.var("rs", rs) + tp.var("cs", cs) + tp.var("origin", origin) + tp.var("fc_index", fc_index) + + if with_left + tp.queue(<<"END") + var tc_box = _frame.bbox; + var tile_box = _tile ? (tc_box & _tile.bbox) : tc_box; + !tile_box.empty && ( + tile_box = tile_box.enlarged(Vector.new(max(rs.x, fc_box.width), max(cs.y, fc_box.height))); + tile_box = tile_box & tc_box; + var left = Region.new; + (region & tile_box).fill(top_cell, fc_index, fc_box, rs, cs, origin, left, Vector.new, left, _tile.bbox); + _output(#{result_arg}, left) + ) +END + else + tp.queue(<<"END") + var tc_box = _frame.bbox; + var tile_box = _tile ? (tc_box & _tile.bbox) : tc_box; + !tile_box.empty && ( + tile_box.right = tile_box.right + rs.x - 1; + tile_box.top = tile_box.top + cs.y - 1; + tile_box = tile_box & tc_box; + (region & tile_box).fill(top_cell, fc_index, fc_box, rs, cs, origin, nil, Vector.new, nil, _tile.bbox) + ) +END + end + + begin + @engine._output_layout.start_changes + @engine.run_timed("\"#{m}\" in: #{@engine.src_line}", self.data) do + tp.execute("Tiled \"#{m}\" in: #{@engine.src_line}") + end + ensure + @engine._output_layout.end_changes + end + + else + + if with_left + result = RBA::Region::new + end + + @engine.run_timed("\"#{m}\" in: #{@engine.src_line}", self.data) do + self.data.fill(top_cell, fc_index, fc_box, rs, cs, origin, result, RBA::Vector::new, result) + end + + end + + self.data.disable_progress + + return result ? DRCLayer::new(@engine, result) : nil + + end # %DRC% # @name data diff --git a/src/drc/drc/built-in-macros/_drc_source.rb b/src/drc/drc/built-in-macros/_drc_source.rb index 4750c0b1f..538ecf968 100644 --- a/src/drc/drc/built-in-macros/_drc_source.rb +++ b/src/drc/drc/built-in-macros/_drc_source.rb @@ -27,6 +27,7 @@ module DRC @clip = false @overlapping = false @tmp_layers = [] + @global_trans = RBA::DCplxTrans::new end # Conceptual deep copy (not including the temp layers) @@ -70,6 +71,24 @@ module DRC def cell_obj @cell end + + def inplace_global_transform(*args) + gt = RBA::DCplxTrans::new + args.each do |a| + if a.is_a?(RBA::DVector) || a.is_a?(RBA::DTrans) + gt = RBA::DCplxTrans::new(a) * gt + elsif a.is_a?(RBA::DCplxTrans) + gt = a * gt + else + raise("Expected a transformation spec instead of #{a.inspect}") + end + end + @global_trans = gt + end + + def global_transformation + @global_trans + end def finish @tmp_layers.each do |li| @@ -245,8 +264,40 @@ module DRC # \touching is a similar method which delivers shapes touching # the search region with their bounding box (without the requirement to overlap) + # %DRC% + # @name global_transform + # @brief Gets or sets a global transformation + # @synopsis global_transform + # @synopsis global_transform([ transformations ]) + # + # This method returns a new source representing the transformed layout. It is provided in the spritit of + # \Source#clip and similar methods. + # + # The transformation + # is either given as a RBA::DTrans, RBA::DVector or RBA::DCplxTrans object or as one of the + # following specifications: + # + # @ul + # @li "shift(x, y)": shifts the input layout horizontally by x and vertically by y micrometers @/li + # @li "rotate(a)": rotates the input layout by a degree counter-clockwise @/li + # @li "magnify(m)": magnifies the input layout by the factor m (NOTE: using fractional scale factors may result in small gaps due to grid snapping) @/li + # @li "mirror_x": mirrors the input layout at the x axis @/li + # @li "mirror_y": mirrors the input layout at the y axis @/li + # @/ul + # + # Multiple transformation specs can be given. In that case the transformations are applied right to left. + # Using "global_transform" will reset any global transformation present already. + # Without an argument, the global transformation is reset. + # + # The following example rotates the layout by 90 degree at the origin (0, 0) and then shifts it up by + # 100 micrometers: + # + # @code + # source.global_transform(shift(0, 100.um), rotate(90.0)) + # @/code + # export inplace_* as * out-of-place - %w(select cell clip touching overlapping).each do |f| + %w(select cell clip touching overlapping global_transform).each do |f| eval <<"CODE" def #{f}(*args) @engine._context("#{f}") do @@ -274,7 +325,7 @@ CODE if @box layer.insert(RBA::DBox::from_ibox(@box) * @layout.dbu) else - layer.insert(RBA::DBox::from_ibox(@cell.bbox) * @layout.dbu) + layer.insert((RBA::DBox::from_ibox(@cell.bbox) * @layout.dbu).transformed(@global_trans)) end layer end @@ -326,7 +377,7 @@ CODE def input(*args) @engine._context("input") do layers = parse_input_layers(*args) - DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SAll, RBA::Region)) + DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SAll, @global_trans, RBA::Region)) end end @@ -350,7 +401,7 @@ CODE def labels(*args) @engine._context("labels") do layers = parse_input_layers(*args) - DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::STexts, RBA::Texts)) + DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::STexts, @global_trans, RBA::Texts)) end end @@ -373,7 +424,7 @@ CODE def polygons(*args) @engine._context("polygons") do layers = parse_input_layers(*args) - DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SBoxes | RBA::Shapes::SPaths | RBA::Shapes::SPolygons | RBA::Shapes::SEdgePairs, RBA::Region)) + DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SBoxes | RBA::Shapes::SPaths | RBA::Shapes::SPolygons | RBA::Shapes::SEdgePairs, @global_trans, RBA::Region)) end end @@ -399,7 +450,7 @@ CODE def edges(*args) @engine._context("edges") do layers = parse_input_layers(*args) - DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SBoxes | RBA::Shapes::SPaths | RBA::Shapes::SPolygons | RBA::Shapes::SEdgePairs | RBA::Shapes::SEdges, RBA::Edges)) + DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SBoxes | RBA::Shapes::SPaths | RBA::Shapes::SPolygons | RBA::Shapes::SEdgePairs | RBA::Shapes::SEdges, @global_trans, RBA::Edges)) end end @@ -425,7 +476,7 @@ CODE def edge_pairs(*args) @engine._context("edge_pairs") do layers = parse_input_layers(*args) - DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SEdgePairs, RBA::EdgePairs)) + DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SEdgePairs, @global_trans, RBA::EdgePairs)) end end @@ -437,7 +488,7 @@ CODE def make_layer layers = [] - DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SAll, RBA::Region)) + DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SAll, @global_trans, RBA::Region)) end # %DRC% diff --git a/src/drc/drc/built-in-macros/_drc_tags.rb b/src/drc/drc/built-in-macros/_drc_tags.rb index c6070cef1..283a7813d 100644 --- a/src/drc/drc/built-in-macros/_drc_tags.rb +++ b/src/drc/drc/built-in-macros/_drc_tags.rb @@ -181,6 +181,177 @@ module DRC end end + # A wrapper for the fill cell definition + class DRCFillCell + + def initialize(name) + @cell_name = name + @shapes = [] + @origin = nil + @dim = nil + end + + def create_cell(layout, engine) + cell = layout.create_cell(@cell_name) + @shapes.each do |s| + li = layout.layer(s[0]) + engine._use_output_layer(li) + s[1].each { |t| cell.shapes(li).insert(t) } + end + cell + end + + def cell_box(def_w, def_h) + o = @origin || self._computed_origin + d = @dim || RBA::DVector::new(def_w, def_h) + RBA::DBox::new(o, o + d) + end + + def default_xpitch + @dim ? @dim.x : self.bbox.width + end + + def default_ypitch + @dim ? @dim.y : self.bbox.height + end + + def _computed_origin + b = self.bbox + return b.empty? ? RBA::DPoint::new : b.p1 + end + + def bbox + box = RBA::DBox::new + @shapes.each do |s| + s[1].each { |t| box += t.bbox } + end + box + end + + def shape(*args) + + layer = nil + datatype = nil + name = nil + shapes = [] + + args.each_with_index do |a,ai| + if a.is_a?(1.class) + if !layer + layer = a + elsif !datatype + datatype = a + else + raise("Argument ##{ai+1} not understood for FillCell#shape") + end + elsif a.is_a?(String) + if !name + name = a + else + raise("Argument ##{ai+1} not understood for FillCell#shape") + end + elsif a.is_a?(RBA::DBox) || a.is_a?(RBA::DPath) || a.is_a?(RBA::DPolygon) || a.is_a?(RBA::DText) + shapes << a + else + raise("Argument ##{ai+1} not understood for FillCell#shape (needs to one of: number, string or box, path, polygon or text)") + end + end + + if !shapes.empty? + + li = RBA::LayerInfo::new + if layer + li.layer = layer + li.datatype = datatype || 0 + end + if name + li.name = name + end + + @shapes << [ li, shapes ] + + end + + self + + end + + def origin(x, y) + + if !x.is_a?(1.class) && !x.is_a?(1.0.class) + raise("x argument not numeric FillCell#origin") + end + if !y.is_a?(1.class) && !y.is_a?(1.0.class) + raise("y argument not numeric FillCell#origin") + end + @origin = RBA::DVector::new(x, y) + + self + + end + + def dim(w, h) + + if !w.is_a?(1.class) && !w.is_a?(1.0.class) + raise("w argument not numeric FillCell#dim") + end + if !h.is_a?(1.class) && !h.is_a?(1.0.class) + raise("h argument not numeric FillCell#dim") + end + @dim = RBA::DVector::new(w, h) + + self + + end + + end + + # A wrapper for the fill step definition + class DRCFillStep + def initialize(for_row, x, y = nil) + @for_row = for_row + if !x.is_a?(1.class) && !x.is_a?(1.0.class) + raise("x argument not numeric in fill step") + end + if y && !y.is_a?(1.class) && !y.is_a?(1.0.class) + raise("y argument not numeric in fill step") + end + if y + @step = RBA::DVector::new(x, y) + elsif for_row + @step = RBA::DVector::new(x, 0) + else + @step = RBA::DVector::new(0, x) + end + end + def for_row + @for_row + end + def step + @step + end + end + + # A wrapper for the fill origin definition + class DRCFillOrigin + def initialize(x = nil, y = nil) + if !x && !y + @origin = nil + else + if !x.is_a?(1.class) && !x.is_a?(1.0.class) + raise("x argument not numeric in fill origin") + end + if !y.is_a?(1.class) && !y.is_a?(1.0.class) + raise("y argument not numeric in fill origin") + end + @origin = RBA::DVector::new(x, y) + end + end + def origin + @origin + end + end + # A wrapper for the tile_size option class DRCTileSize def initialize(*args) @@ -230,6 +401,6 @@ module DRC @b end end - + end diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index b1508b821..36b46732a 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -1168,3 +1168,57 @@ TEST(30_density) run_test (_this, "30", false); } +TEST(31_globaTransformation) +{ + run_test (_this, "31", false); +} + +TEST(31d_globalTransformation) +{ + run_test (_this, "31", true); +} + +TEST(32_globalTransformationWithClip) +{ + run_test (_this, "32", false); +} + +TEST(32d_globalTransformationWithClip) +{ + run_test (_this, "32", true); +} + +TEST(33_globalTransformationWithTiles) +{ + run_test (_this, "33", true); +} + +TEST(40_fill) +{ + run_test (_this, "40", false); +} + +TEST(41_fillTiled) +{ + run_test (_this, "41", false); +} + +TEST(42_fillWithLeft) +{ + run_test (_this, "42", false); +} + +TEST(43_fillWithLeftTiled) +{ + run_test (_this, "43", false); +} + +TEST(44_fillWithOverlappingBoxes) +{ + run_test (_this, "44", false); +} + +TEST(45_fillWithOverlappingBoxesTiled) +{ + run_test (_this, "45", false); +} diff --git a/src/gsi/gsi/gsiExpression.cc b/src/gsi/gsi/gsiExpression.cc index f27433a35..b844158a6 100644 --- a/src/gsi/gsi/gsiExpression.cc +++ b/src/gsi/gsi/gsiExpression.cc @@ -1504,9 +1504,14 @@ VariantUserClassImpl::execute_gsi (const tl::ExpressionParserContext & /*context tl::Heap heap; size_t narg = 0; for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments () && narg < args.size (); ++a, ++narg) { - // Note: this const_cast is ugly, but it will basically enable "out" parameters - // TODO: clean this up. - gsi::do_on_type () (a->type (), &arglist, const_cast (&args [narg]), *a, &heap); + try { + // Note: this const_cast is ugly, but it will basically enable "out" parameters + // TODO: clean this up. + gsi::do_on_type () (a->type (), &arglist, const_cast (&args [narg]), *a, &heap); + } catch (tl::Exception &ex) { + std::string msg = ex.msg () + tl::sprintf (tl::to_string (tr (" (argument '%s')")), a->spec ()->name ()); + throw tl::Exception (msg); + } } SerialArgs retlist (meth->retsize ()); @@ -1518,7 +1523,12 @@ VariantUserClassImpl::execute_gsi (const tl::ExpressionParserContext & /*context throw tl::Exception (tl::sprintf (tl::to_string (tr ("Iterators not supported yet (method %s, class %s)")), method.c_str (), mp_cls->name ())); } else { out = tl::Variant (); - gsi::do_on_type () (meth->ret_type ().type (), &out, &retlist, meth->ret_type (), &heap); + try { + gsi::do_on_type () (meth->ret_type ().type (), &out, &retlist, meth->ret_type (), &heap); + } catch (tl::Exception &ex) { + std::string msg = ex.msg () + tl::to_string (tr (" (return value)")); + throw tl::Exception (msg); + } } } diff --git a/src/gsi/gsi/gsiMethods.h b/src/gsi/gsi/gsiMethods.h index 169e540c9..4a12754a1 100644 --- a/src/gsi/gsi/gsiMethods.h +++ b/src/gsi/gsi/gsiMethods.h @@ -939,7 +939,7 @@ constant (const std::string &name, const R &v, const std::string &doc = std::str #define _FUNCARGLIST A1 #define _ADDARGS this->template add_arg (m_s1); #define _GETARGVARS tl::Heap heap;\ - A1 a1 = args ? args.template read (heap) : m_s1.init (); + A1 a1 = args ? args.template read (heap, &m_s1) : m_s1.init (); #define _ARGVARLIST a1 #define _ARGSPECARGS s1 #define _ARGSPEC const ArgSpec &s1 @@ -973,8 +973,8 @@ constant (const std::string &name, const R &v, const std::string &doc = std::str #define _ADDARGS this->template add_arg (m_s1); \ this->template add_arg (m_s2); #define _GETARGVARS tl::Heap heap;\ - A1 a1 = args ? args.template read (heap) : m_s1.init (); \ - A2 a2 = args ? args.template read (heap) : m_s2.init (); + A1 a1 = args ? args.template read (heap, &m_s1) : m_s1.init (); \ + A2 a2 = args ? args.template read (heap, &m_s2) : m_s2.init (); #define _ARGVARLIST a1, a2 #define _ARGSPECARGS s1, s2 #define _ARGSPEC const ArgSpec &s1, const ArgSpec &s2 @@ -1010,9 +1010,9 @@ constant (const std::string &name, const R &v, const std::string &doc = std::str this->template add_arg (m_s2); \ this->template add_arg (m_s3); #define _GETARGVARS tl::Heap heap;\ - A1 a1 = args ? args.template read (heap) : m_s1.init (); \ - A2 a2 = args ? args.template read (heap) : m_s2.init (); \ - A3 a3 = args ? args.template read (heap) : m_s3.init (); + A1 a1 = args ? args.template read (heap, &m_s1) : m_s1.init (); \ + A2 a2 = args ? args.template read (heap, &m_s2) : m_s2.init (); \ + A3 a3 = args ? args.template read (heap, &m_s3) : m_s3.init (); #define _ARGVARLIST a1, a2, a3 #define _ARGSPECARGS s1, s2, s3 #define _ARGSPEC const ArgSpec &s1, const ArgSpec &s2, const ArgSpec &s3 @@ -1050,10 +1050,10 @@ constant (const std::string &name, const R &v, const std::string &doc = std::str this->template add_arg (m_s3); \ this->template add_arg (m_s4); #define _GETARGVARS tl::Heap heap;\ - A1 a1 = args ? args.template read (heap) : m_s1.init (); \ - A2 a2 = args ? args.template read (heap) : m_s2.init (); \ - A3 a3 = args ? args.template read (heap) : m_s3.init (); \ - A4 a4 = args ? args.template read (heap) : m_s4.init (); + A1 a1 = args ? args.template read (heap, &m_s1) : m_s1.init (); \ + A2 a2 = args ? args.template read (heap, &m_s2) : m_s2.init (); \ + A3 a3 = args ? args.template read (heap, &m_s3) : m_s3.init (); \ + A4 a4 = args ? args.template read (heap, &m_s4) : m_s4.init (); #define _ARGVARLIST a1, a2, a3, a4 #define _ARGSPECARGS s1, s2, s3, s4 #define _ARGSPEC const ArgSpec &s1, const ArgSpec &s2, const ArgSpec &s3, const ArgSpec &s4 @@ -1093,11 +1093,11 @@ constant (const std::string &name, const R &v, const std::string &doc = std::str this->template add_arg (m_s4); \ this->template add_arg (m_s5); #define _GETARGVARS tl::Heap heap;\ - A1 a1 = args ? args.template read (heap) : m_s1.init (); \ - A2 a2 = args ? args.template read (heap) : m_s2.init (); \ - A3 a3 = args ? args.template read (heap) : m_s3.init (); \ - A4 a4 = args ? args.template read (heap) : m_s4.init (); \ - A5 a5 = args ? args.template read (heap) : m_s5.init (); + A1 a1 = args ? args.template read (heap, &m_s1) : m_s1.init (); \ + A2 a2 = args ? args.template read (heap, &m_s2) : m_s2.init (); \ + A3 a3 = args ? args.template read (heap, &m_s3) : m_s3.init (); \ + A4 a4 = args ? args.template read (heap, &m_s4) : m_s4.init (); \ + A5 a5 = args ? args.template read (heap, &m_s5) : m_s5.init (); #define _ARGVARLIST a1, a2, a3, a4, a5 #define _ARGSPECARGS s1, s2, s3, s4, s5 #define _ARGSPEC const ArgSpec &s1, const ArgSpec &s2, const ArgSpec &s3, const ArgSpec &s4, const ArgSpec &s5 @@ -1139,12 +1139,12 @@ constant (const std::string &name, const R &v, const std::string &doc = std::str this->template add_arg (m_s5); \ this->template add_arg (m_s6); #define _GETARGVARS tl::Heap heap;\ - A1 a1 = args ? args.template read (heap) : m_s1.init (); \ - A2 a2 = args ? args.template read (heap) : m_s2.init (); \ - A3 a3 = args ? args.template read (heap) : m_s3.init (); \ - A4 a4 = args ? args.template read (heap) : m_s4.init (); \ - A5 a5 = args ? args.template read (heap) : m_s5.init (); \ - A6 a6 = args ? args.template read (heap) : m_s6.init (); + A1 a1 = args ? args.template read (heap, &m_s1) : m_s1.init (); \ + A2 a2 = args ? args.template read (heap, &m_s2) : m_s2.init (); \ + A3 a3 = args ? args.template read (heap, &m_s3) : m_s3.init (); \ + A4 a4 = args ? args.template read (heap, &m_s4) : m_s4.init (); \ + A5 a5 = args ? args.template read (heap, &m_s5) : m_s5.init (); \ + A6 a6 = args ? args.template read (heap, &m_s6) : m_s6.init (); #define _ARGVARLIST a1, a2, a3, a4, a5, a6 #define _ARGSPECARGS s1, s2, s3, s4, s5, s6 #define _ARGSPEC const ArgSpec &s1, const ArgSpec &s2, const ArgSpec &s3, const ArgSpec &s4, const ArgSpec &s5, const ArgSpec &s6 @@ -1188,13 +1188,13 @@ constant (const std::string &name, const R &v, const std::string &doc = std::str this->template add_arg (m_s6); \ this->template add_arg (m_s7); #define _GETARGVARS tl::Heap heap;\ - A1 a1 = args ? args.template read (heap) : m_s1.init (); \ - A2 a2 = args ? args.template read (heap) : m_s2.init (); \ - A3 a3 = args ? args.template read (heap) : m_s3.init (); \ - A4 a4 = args ? args.template read (heap) : m_s4.init (); \ - A5 a5 = args ? args.template read (heap) : m_s5.init (); \ - A6 a6 = args ? args.template read (heap) : m_s6.init (); \ - A7 a7 = args ? args.template read (heap) : m_s7.init (); + A1 a1 = args ? args.template read (heap, &m_s1) : m_s1.init (); \ + A2 a2 = args ? args.template read (heap, &m_s2) : m_s2.init (); \ + A3 a3 = args ? args.template read (heap, &m_s3) : m_s3.init (); \ + A4 a4 = args ? args.template read (heap, &m_s4) : m_s4.init (); \ + A5 a5 = args ? args.template read (heap, &m_s5) : m_s5.init (); \ + A6 a6 = args ? args.template read (heap, &m_s6) : m_s6.init (); \ + A7 a7 = args ? args.template read (heap, &m_s7) : m_s7.init (); #define _ARGVARLIST a1, a2, a3, a4, a5, a6, a7 #define _ARGSPECARGS s1, s2, s3, s4, s5, s6, s7 #define _ARGSPEC const ArgSpec &s1, const ArgSpec &s2, const ArgSpec &s3, const ArgSpec &s4, const ArgSpec &s5, const ArgSpec &s6, const ArgSpec &s7 @@ -1240,14 +1240,14 @@ constant (const std::string &name, const R &v, const std::string &doc = std::str this->template add_arg (m_s7); \ this->template add_arg (m_s8); #define _GETARGVARS tl::Heap heap;\ - A1 a1 = args ? args.template read (heap) : m_s1.init (); \ - A2 a2 = args ? args.template read (heap) : m_s2.init (); \ - A3 a3 = args ? args.template read (heap) : m_s3.init (); \ - A4 a4 = args ? args.template read (heap) : m_s4.init (); \ - A5 a5 = args ? args.template read (heap) : m_s5.init (); \ - A6 a6 = args ? args.template read (heap) : m_s6.init (); \ - A7 a7 = args ? args.template read (heap) : m_s7.init (); \ - A8 a8 = args ? args.template read (heap) : m_s8.init (); + A1 a1 = args ? args.template read (heap, &m_s1) : m_s1.init (); \ + A2 a2 = args ? args.template read (heap, &m_s2) : m_s2.init (); \ + A3 a3 = args ? args.template read (heap, &m_s3) : m_s3.init (); \ + A4 a4 = args ? args.template read (heap, &m_s4) : m_s4.init (); \ + A5 a5 = args ? args.template read (heap, &m_s5) : m_s5.init (); \ + A6 a6 = args ? args.template read (heap, &m_s6) : m_s6.init (); \ + A7 a7 = args ? args.template read (heap, &m_s7) : m_s7.init (); \ + A8 a8 = args ? args.template read (heap, &m_s8) : m_s8.init (); #define _ARGVARLIST a1, a2, a3, a4, a5, a6, a7, a8 #define _ARGSPECARGS s1, s2, s3, s4, s5, s6, s7, s8 #define _ARGSPEC const ArgSpec &s1, const ArgSpec &s2, const ArgSpec &s3, const ArgSpec &s4, const ArgSpec &s5, const ArgSpec &s6, const ArgSpec &s7, const ArgSpec &s8 @@ -1295,15 +1295,15 @@ constant (const std::string &name, const R &v, const std::string &doc = std::str this->template add_arg (m_s8); \ this->template add_arg (m_s9); #define _GETARGVARS tl::Heap heap;\ - A1 a1 = args ? args.template read (heap) : m_s1.init (); \ - A2 a2 = args ? args.template read (heap) : m_s2.init (); \ - A3 a3 = args ? args.template read (heap) : m_s3.init (); \ - A4 a4 = args ? args.template read (heap) : m_s4.init (); \ - A5 a5 = args ? args.template read (heap) : m_s5.init (); \ - A6 a6 = args ? args.template read (heap) : m_s6.init (); \ - A7 a7 = args ? args.template read (heap) : m_s7.init (); \ - A8 a8 = args ? args.template read (heap) : m_s8.init (); \ - A9 a9 = args ? args.template read (heap) : m_s9.init (); + A1 a1 = args ? args.template read (heap, &m_s1) : m_s1.init (); \ + A2 a2 = args ? args.template read (heap, &m_s2) : m_s2.init (); \ + A3 a3 = args ? args.template read (heap, &m_s3) : m_s3.init (); \ + A4 a4 = args ? args.template read (heap, &m_s4) : m_s4.init (); \ + A5 a5 = args ? args.template read (heap, &m_s5) : m_s5.init (); \ + A6 a6 = args ? args.template read (heap, &m_s6) : m_s6.init (); \ + A7 a7 = args ? args.template read (heap, &m_s7) : m_s7.init (); \ + A8 a8 = args ? args.template read (heap, &m_s8) : m_s8.init (); \ + A9 a9 = args ? args.template read (heap, &m_s9) : m_s9.init (); #define _ARGVARLIST a1, a2, a3, a4, a5, a6, a7, a8, a9 #define _ARGSPECARGS s1, s2, s3, s4, s5, s6, s7, s8, s9 #define _ARGSPEC const ArgSpec &s1, const ArgSpec &s2, const ArgSpec &s3, const ArgSpec &s4, const ArgSpec &s5, const ArgSpec &s6, const ArgSpec &s7, const ArgSpec &s8, const ArgSpec &s9 @@ -1353,16 +1353,16 @@ constant (const std::string &name, const R &v, const std::string &doc = std::str this->template add_arg (m_s9); \ this->template add_arg (m_s10); #define _GETARGVARS tl::Heap heap;\ - A1 a1 = args ? args.template read (heap) : m_s1.init (); \ - A2 a2 = args ? args.template read (heap) : m_s2.init (); \ - A3 a3 = args ? args.template read (heap) : m_s3.init (); \ - A4 a4 = args ? args.template read (heap) : m_s4.init (); \ - A5 a5 = args ? args.template read (heap) : m_s5.init (); \ - A6 a6 = args ? args.template read (heap) : m_s6.init (); \ - A7 a7 = args ? args.template read (heap) : m_s7.init (); \ - A8 a8 = args ? args.template read (heap) : m_s8.init (); \ - A9 a9 = args ? args.template read (heap) : m_s9.init (); \ - A10 a10 = args ? args.template read (heap) : m_s10.init (); + A1 a1 = args ? args.template read (heap, &m_s1) : m_s1.init (); \ + A2 a2 = args ? args.template read (heap, &m_s2) : m_s2.init (); \ + A3 a3 = args ? args.template read (heap, &m_s3) : m_s3.init (); \ + A4 a4 = args ? args.template read (heap, &m_s4) : m_s4.init (); \ + A5 a5 = args ? args.template read (heap, &m_s5) : m_s5.init (); \ + A6 a6 = args ? args.template read (heap, &m_s6) : m_s6.init (); \ + A7 a7 = args ? args.template read (heap, &m_s7) : m_s7.init (); \ + A8 a8 = args ? args.template read (heap, &m_s8) : m_s8.init (); \ + A9 a9 = args ? args.template read (heap, &m_s9) : m_s9.init (); \ + A10 a10 = args ? args.template read (heap, &m_s10) : m_s10.init (); #define _ARGVARLIST a1, a2, a3, a4, a5, a6, a7, a8, a9, a10 #define _ARGSPECARGS s1, s2, s3, s4, s5, s6, s7, s8, s9, s10 #define _ARGSPEC const ArgSpec &s1, const ArgSpec &s2, const ArgSpec &s3, const ArgSpec &s4, const ArgSpec &s5, const ArgSpec &s6, const ArgSpec &s7, const ArgSpec &s8, const ArgSpec &s9, const ArgSpec &s10 @@ -1414,17 +1414,17 @@ constant (const std::string &name, const R &v, const std::string &doc = std::str this->template add_arg (m_s10); \ this->template add_arg (m_s11); #define _GETARGVARS tl::Heap heap;\ - A1 a1 = args ? args.template read (heap) : m_s1.init (); \ - A2 a2 = args ? args.template read (heap) : m_s2.init (); \ - A3 a3 = args ? args.template read (heap) : m_s3.init (); \ - A4 a4 = args ? args.template read (heap) : m_s4.init (); \ - A5 a5 = args ? args.template read (heap) : m_s5.init (); \ - A6 a6 = args ? args.template read (heap) : m_s6.init (); \ - A7 a7 = args ? args.template read (heap) : m_s7.init (); \ - A8 a8 = args ? args.template read (heap) : m_s8.init (); \ - A9 a9 = args ? args.template read (heap) : m_s9.init (); \ - A10 a10 = args ? args.template read (heap) : m_s10.init (); \ - A11 a11 = args ? args.template read (heap) : m_s11.init (); + A1 a1 = args ? args.template read (heap, &m_s1) : m_s1.init (); \ + A2 a2 = args ? args.template read (heap, &m_s2) : m_s2.init (); \ + A3 a3 = args ? args.template read (heap, &m_s3) : m_s3.init (); \ + A4 a4 = args ? args.template read (heap, &m_s4) : m_s4.init (); \ + A5 a5 = args ? args.template read (heap, &m_s5) : m_s5.init (); \ + A6 a6 = args ? args.template read (heap, &m_s6) : m_s6.init (); \ + A7 a7 = args ? args.template read (heap, &m_s7) : m_s7.init (); \ + A8 a8 = args ? args.template read (heap, &m_s8) : m_s8.init (); \ + A9 a9 = args ? args.template read (heap, &m_s9) : m_s9.init (); \ + A10 a10 = args ? args.template read (heap, &m_s10) : m_s10.init (); \ + A11 a11 = args ? args.template read (heap, &m_s11) : m_s11.init (); #define _ARGVARLIST a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11 #define _ARGSPECARGS s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11 #define _ARGSPEC const ArgSpec &s1, const ArgSpec &s2, const ArgSpec &s3, const ArgSpec &s4, const ArgSpec &s5, const ArgSpec &s6, const ArgSpec &s7, const ArgSpec &s8, const ArgSpec &s9, const ArgSpec &s10, const ArgSpec &s11 @@ -1478,18 +1478,18 @@ constant (const std::string &name, const R &v, const std::string &doc = std::str this->template add_arg (m_s11); \ this->template add_arg (m_s12); #define _GETARGVARS tl::Heap heap;\ - A1 a1 = args ? args.template read (heap) : m_s1.init (); \ - A2 a2 = args ? args.template read (heap) : m_s2.init (); \ - A3 a3 = args ? args.template read (heap) : m_s3.init (); \ - A4 a4 = args ? args.template read (heap) : m_s4.init (); \ - A5 a5 = args ? args.template read (heap) : m_s5.init (); \ - A6 a6 = args ? args.template read (heap) : m_s6.init (); \ - A7 a7 = args ? args.template read (heap) : m_s7.init (); \ - A8 a8 = args ? args.template read (heap) : m_s8.init (); \ - A9 a9 = args ? args.template read (heap) : m_s9.init (); \ - A10 a10 = args ? args.template read (heap) : m_s10.init (); \ - A11 a11 = args ? args.template read (heap) : m_s11.init (); \ - A12 a12 = args ? args.template read (heap) : m_s12.init (); + A1 a1 = args ? args.template read (heap, &m_s1) : m_s1.init (); \ + A2 a2 = args ? args.template read (heap, &m_s2) : m_s2.init (); \ + A3 a3 = args ? args.template read (heap, &m_s3) : m_s3.init (); \ + A4 a4 = args ? args.template read (heap, &m_s4) : m_s4.init (); \ + A5 a5 = args ? args.template read (heap, &m_s5) : m_s5.init (); \ + A6 a6 = args ? args.template read (heap, &m_s6) : m_s6.init (); \ + A7 a7 = args ? args.template read (heap, &m_s7) : m_s7.init (); \ + A8 a8 = args ? args.template read (heap, &m_s8) : m_s8.init (); \ + A9 a9 = args ? args.template read (heap, &m_s9) : m_s9.init (); \ + A10 a10 = args ? args.template read (heap, &m_s10) : m_s10.init (); \ + A11 a11 = args ? args.template read (heap, &m_s11) : m_s11.init (); \ + A12 a12 = args ? args.template read (heap, &m_s12) : m_s12.init (); #define _ARGVARLIST a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 #define _ARGSPECARGS s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12 #define _ARGSPEC const ArgSpec &s1, const ArgSpec &s2, const ArgSpec &s3, const ArgSpec &s4, const ArgSpec &s5, const ArgSpec &s6, const ArgSpec &s7, const ArgSpec &s8, const ArgSpec &s9, const ArgSpec &s10, const ArgSpec &s11, const ArgSpec &s12 @@ -1545,19 +1545,19 @@ constant (const std::string &name, const R &v, const std::string &doc = std::str this->template add_arg (m_s12); \ this->template add_arg (m_s13); #define _GETARGVARS tl::Heap heap;\ - A1 a1 = args ? args.template read (heap) : m_s1.init (); \ - A2 a2 = args ? args.template read (heap) : m_s2.init (); \ - A3 a3 = args ? args.template read (heap) : m_s3.init (); \ - A4 a4 = args ? args.template read (heap) : m_s4.init (); \ - A5 a5 = args ? args.template read (heap) : m_s5.init (); \ - A6 a6 = args ? args.template read (heap) : m_s6.init (); \ - A7 a7 = args ? args.template read (heap) : m_s7.init (); \ - A8 a8 = args ? args.template read (heap) : m_s8.init (); \ - A9 a9 = args ? args.template read (heap) : m_s9.init (); \ - A10 a10 = args ? args.template read (heap) : m_s10.init (); \ - A11 a11 = args ? args.template read (heap) : m_s11.init (); \ - A12 a12 = args ? args.template read (heap) : m_s12.init (); \ - A13 a13 = args ? args.template read (heap) : m_s13.init (); + A1 a1 = args ? args.template read (heap, &m_s1) : m_s1.init (); \ + A2 a2 = args ? args.template read (heap, &m_s2) : m_s2.init (); \ + A3 a3 = args ? args.template read (heap, &m_s3) : m_s3.init (); \ + A4 a4 = args ? args.template read (heap, &m_s4) : m_s4.init (); \ + A5 a5 = args ? args.template read (heap, &m_s5) : m_s5.init (); \ + A6 a6 = args ? args.template read (heap, &m_s6) : m_s6.init (); \ + A7 a7 = args ? args.template read (heap, &m_s7) : m_s7.init (); \ + A8 a8 = args ? args.template read (heap, &m_s8) : m_s8.init (); \ + A9 a9 = args ? args.template read (heap, &m_s9) : m_s9.init (); \ + A10 a10 = args ? args.template read (heap, &m_s10) : m_s10.init (); \ + A11 a11 = args ? args.template read (heap, &m_s11) : m_s11.init (); \ + A12 a12 = args ? args.template read (heap, &m_s12) : m_s12.init (); \ + A13 a13 = args ? args.template read (heap, &m_s13) : m_s13.init (); #define _ARGVARLIST a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13 #define _ARGSPECARGS s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13 #define _ARGSPEC const ArgSpec &s1, const ArgSpec &s2, const ArgSpec &s3, const ArgSpec &s4, const ArgSpec &s5, const ArgSpec &s6, const ArgSpec &s7, const ArgSpec &s8, const ArgSpec &s9, const ArgSpec &s10, const ArgSpec &s11, const ArgSpec &s12, const ArgSpec &s13 @@ -1615,20 +1615,20 @@ constant (const std::string &name, const R &v, const std::string &doc = std::str this->template add_arg (m_s13); \ this->template add_arg (m_s14); #define _GETARGVARS tl::Heap heap;\ - A1 a1 = args ? args.template read (heap) : m_s1.init (); \ - A2 a2 = args ? args.template read (heap) : m_s2.init (); \ - A3 a3 = args ? args.template read (heap) : m_s3.init (); \ - A4 a4 = args ? args.template read (heap) : m_s4.init (); \ - A5 a5 = args ? args.template read (heap) : m_s5.init (); \ - A6 a6 = args ? args.template read (heap) : m_s6.init (); \ - A7 a7 = args ? args.template read (heap) : m_s7.init (); \ - A8 a8 = args ? args.template read (heap) : m_s8.init (); \ - A9 a9 = args ? args.template read (heap) : m_s9.init (); \ - A10 a10 = args ? args.template read (heap) : m_s10.init (); \ - A11 a11 = args ? args.template read (heap) : m_s11.init (); \ - A12 a12 = args ? args.template read (heap) : m_s12.init (); \ - A13 a13 = args ? args.template read (heap) : m_s13.init (); \ - A14 a14 = args ? args.template read (heap) : m_s14.init (); + A1 a1 = args ? args.template read (heap, &m_s1) : m_s1.init (); \ + A2 a2 = args ? args.template read (heap, &m_s2) : m_s2.init (); \ + A3 a3 = args ? args.template read (heap, &m_s3) : m_s3.init (); \ + A4 a4 = args ? args.template read (heap, &m_s4) : m_s4.init (); \ + A5 a5 = args ? args.template read (heap, &m_s5) : m_s5.init (); \ + A6 a6 = args ? args.template read (heap, &m_s6) : m_s6.init (); \ + A7 a7 = args ? args.template read (heap, &m_s7) : m_s7.init (); \ + A8 a8 = args ? args.template read (heap, &m_s8) : m_s8.init (); \ + A9 a9 = args ? args.template read (heap, &m_s9) : m_s9.init (); \ + A10 a10 = args ? args.template read (heap, &m_s10) : m_s10.init (); \ + A11 a11 = args ? args.template read (heap, &m_s11) : m_s11.init (); \ + A12 a12 = args ? args.template read (heap, &m_s12) : m_s12.init (); \ + A13 a13 = args ? args.template read (heap, &m_s13) : m_s13.init (); \ + A14 a14 = args ? args.template read (heap, &m_s14) : m_s14.init (); #define _ARGVARLIST a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14 #define _ARGSPECARGS s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14 #define _ARGSPEC const ArgSpec &s1, const ArgSpec &s2, const ArgSpec &s3, const ArgSpec &s4, const ArgSpec &s5, const ArgSpec &s6, const ArgSpec &s7, const ArgSpec &s8, const ArgSpec &s9, const ArgSpec &s10, const ArgSpec &s11, const ArgSpec &s12, const ArgSpec &s13, const ArgSpec &s14 @@ -1688,21 +1688,21 @@ constant (const std::string &name, const R &v, const std::string &doc = std::str this->template add_arg (m_s14); \ this->template add_arg (m_s15); #define _GETARGVARS tl::Heap heap;\ - A1 a1 = args ? args.template read (heap) : m_s1.init (); \ - A2 a2 = args ? args.template read (heap) : m_s2.init (); \ - A3 a3 = args ? args.template read (heap) : m_s3.init (); \ - A4 a4 = args ? args.template read (heap) : m_s4.init (); \ - A5 a5 = args ? args.template read (heap) : m_s5.init (); \ - A6 a6 = args ? args.template read (heap) : m_s6.init (); \ - A7 a7 = args ? args.template read (heap) : m_s7.init (); \ - A8 a8 = args ? args.template read (heap) : m_s8.init (); \ - A9 a9 = args ? args.template read (heap) : m_s9.init (); \ - A10 a10 = args ? args.template read (heap) : m_s10.init (); \ - A11 a11 = args ? args.template read (heap) : m_s11.init (); \ - A12 a12 = args ? args.template read (heap) : m_s12.init (); \ - A13 a13 = args ? args.template read (heap) : m_s13.init (); \ - A14 a14 = args ? args.template read (heap) : m_s14.init (); \ - A15 a15 = args ? args.template read (heap) : m_s15.init (); + A1 a1 = args ? args.template read (heap, &m_s1) : m_s1.init (); \ + A2 a2 = args ? args.template read (heap, &m_s2) : m_s2.init (); \ + A3 a3 = args ? args.template read (heap, &m_s3) : m_s3.init (); \ + A4 a4 = args ? args.template read (heap, &m_s4) : m_s4.init (); \ + A5 a5 = args ? args.template read (heap, &m_s5) : m_s5.init (); \ + A6 a6 = args ? args.template read (heap, &m_s6) : m_s6.init (); \ + A7 a7 = args ? args.template read (heap, &m_s7) : m_s7.init (); \ + A8 a8 = args ? args.template read (heap, &m_s8) : m_s8.init (); \ + A9 a9 = args ? args.template read (heap, &m_s9) : m_s9.init (); \ + A10 a10 = args ? args.template read (heap, &m_s10) : m_s10.init (); \ + A11 a11 = args ? args.template read (heap, &m_s11) : m_s11.init (); \ + A12 a12 = args ? args.template read (heap, &m_s12) : m_s12.init (); \ + A13 a13 = args ? args.template read (heap, &m_s13) : m_s13.init (); \ + A14 a14 = args ? args.template read (heap, &m_s14) : m_s14.init (); \ + A15 a15 = args ? args.template read (heap, &m_s15) : m_s15.init (); #define _ARGVARLIST a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 #define _ARGSPECARGS s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15 #define _ARGSPEC const ArgSpec &s1, const ArgSpec &s2, const ArgSpec &s3, const ArgSpec &s4, const ArgSpec &s5, const ArgSpec &s6, const ArgSpec &s7, const ArgSpec &s8, const ArgSpec &s9, const ArgSpec &s10, const ArgSpec &s11, const ArgSpec &s12, const ArgSpec &s13, const ArgSpec &s14, const ArgSpec &s15 @@ -1764,22 +1764,22 @@ constant (const std::string &name, const R &v, const std::string &doc = std::str this->template add_arg (m_s15); \ this->template add_arg (m_s16); #define _GETARGVARS tl::Heap heap;\ - A1 a1 = args ? args.template read (heap) : m_s1.init (); \ - A2 a2 = args ? args.template read (heap) : m_s2.init (); \ - A3 a3 = args ? args.template read (heap) : m_s3.init (); \ - A4 a4 = args ? args.template read (heap) : m_s4.init (); \ - A5 a5 = args ? args.template read (heap) : m_s5.init (); \ - A6 a6 = args ? args.template read (heap) : m_s6.init (); \ - A7 a7 = args ? args.template read (heap) : m_s7.init (); \ - A8 a8 = args ? args.template read (heap) : m_s8.init (); \ - A9 a9 = args ? args.template read (heap) : m_s9.init (); \ - A10 a10 = args ? args.template read (heap) : m_s10.init (); \ - A11 a11 = args ? args.template read (heap) : m_s11.init (); \ - A12 a12 = args ? args.template read (heap) : m_s12.init (); \ - A13 a13 = args ? args.template read (heap) : m_s13.init (); \ - A14 a14 = args ? args.template read (heap) : m_s14.init (); \ - A15 a15 = args ? args.template read (heap) : m_s15.init (); \ - A16 a16 = args ? args.template read (heap) : m_s16.init (); + A1 a1 = args ? args.template read (heap, &m_s1) : m_s1.init (); \ + A2 a2 = args ? args.template read (heap, &m_s2) : m_s2.init (); \ + A3 a3 = args ? args.template read (heap, &m_s3) : m_s3.init (); \ + A4 a4 = args ? args.template read (heap, &m_s4) : m_s4.init (); \ + A5 a5 = args ? args.template read (heap, &m_s5) : m_s5.init (); \ + A6 a6 = args ? args.template read (heap, &m_s6) : m_s6.init (); \ + A7 a7 = args ? args.template read (heap, &m_s7) : m_s7.init (); \ + A8 a8 = args ? args.template read (heap, &m_s8) : m_s8.init (); \ + A9 a9 = args ? args.template read (heap, &m_s9) : m_s9.init (); \ + A10 a10 = args ? args.template read (heap, &m_s10) : m_s10.init (); \ + A11 a11 = args ? args.template read (heap, &m_s11) : m_s11.init (); \ + A12 a12 = args ? args.template read (heap, &m_s12) : m_s12.init (); \ + A13 a13 = args ? args.template read (heap, &m_s13) : m_s13.init (); \ + A14 a14 = args ? args.template read (heap, &m_s14) : m_s14.init (); \ + A15 a15 = args ? args.template read (heap, &m_s15) : m_s15.init (); \ + A16 a16 = args ? args.template read (heap, &m_s16) : m_s16.init (); #define _ARGVARLIST a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16 #define _ARGSPECARGS s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16 #define _ARGSPEC const ArgSpec &s1, const ArgSpec &s2, const ArgSpec &s3, const ArgSpec &s4, const ArgSpec &s5, const ArgSpec &s6, const ArgSpec &s7, const ArgSpec &s8, const ArgSpec &s9, const ArgSpec &s10, const ArgSpec &s11, const ArgSpec &s12, const ArgSpec &s13, const ArgSpec &s14, const ArgSpec &s15, const ArgSpec &s16 diff --git a/src/gsi/gsi/gsiSerialisation.h b/src/gsi/gsi/gsiSerialisation.h index 3801d1553..ef4a9f8a3 100644 --- a/src/gsi/gsi/gsiSerialisation.h +++ b/src/gsi/gsi/gsiSerialisation.h @@ -83,6 +83,17 @@ struct GSI_PUBLIC ArglistUnderflowException { } }; +/** + * @brief An exception thrown if there are not enough arguments on the serialization buffer + */ +struct GSI_PUBLIC ArglistUnderflowExceptionWithType + : public tl::Exception +{ + ArglistUnderflowExceptionWithType (const ArgSpecBase &as) + : tl::Exception (tl::to_string (tr ("Too few arguments - missing '%s'")), as.name ()) + { } +}; + /** * @brief An exception thrown if a reference is null (nil) */ @@ -94,6 +105,17 @@ struct GSI_PUBLIC NilPointerToReference { } }; +/** + * @brief An exception thrown if a reference is null (nil) + */ +struct GSI_PUBLIC NilPointerToReferenceWithType + : public tl::Exception +{ + NilPointerToReferenceWithType (const ArgSpecBase &as) + : tl::Exception (tl::to_string (tr ("nil object passed to a reference for '%s'")), as.name ()) + { } +}; + /** * @brief This class provides the basic argument serialization mechanism for the C++/scripting interface */ @@ -208,7 +230,16 @@ public: template inline X read (tl::Heap &heap) { - return this->read_impl (typename type_traits::tag (), heap); + return this->read_impl (typename type_traits::tag (), heap, 0); + } + + /** + * @brief Reads a value from the buffer + */ + template + inline X read (tl::Heap &heap, const ArgSpecBase *as) + { + return this->read_impl (typename type_traits::tag (), heap, as); } /** @@ -231,6 +262,26 @@ private: } } + inline void check_data (const ArgSpecBase *as) const + { + if (! *this) { + if (as) { + throw ArglistUnderflowExceptionWithType (*as); + } else { + throw ArglistUnderflowException (); + } + } + } + + inline void throw_nil_for_reference (const ArgSpecBase *as) const + { + if (as) { + throw NilPointerToReferenceWithType (*as); + } else { + throw NilPointerToReference (); + } + } + // ----------------------------------------------------------- // reader implementations @@ -369,18 +420,18 @@ private: // reader implementations template - X read_impl (const pod_direct_tag &, tl::Heap &) + X read_impl (const pod_direct_tag &, tl::Heap &, const ArgSpecBase *as) { - check_data (); + check_data (as); X r = *((X *)mp_read); mp_read += item_size (); return r; } template - X read_impl (const x_tag &, tl::Heap &) + X read_impl (const x_tag &, tl::Heap &, const ArgSpecBase *as) { - check_data (); + check_data (as); X *xp = *(X **)mp_read; X x = *xp; delete xp; @@ -389,7 +440,7 @@ private: } template - X read_impl (const vptr_tag &, tl::Heap &) + X read_impl (const vptr_tag &, tl::Heap &, const ArgSpecBase *) { void *r = *((void **)mp_read); mp_read += item_size (); @@ -398,73 +449,73 @@ private: } template - X read_impl (const ref_tag &, tl::Heap &) + X read_impl (const ref_tag &, tl::Heap &, const ArgSpecBase *as) { typedef typename type_traits::value_type value_type; - check_data (); + check_data (as); value_type *r = *((value_type **)mp_read); mp_read += item_size (); if (! r) { - throw NilPointerToReference (); + throw_nil_for_reference (as); } return *r; } template - X read_impl (const pod_cref_tag &, tl::Heap &) + X read_impl (const pod_cref_tag &, tl::Heap &, const ArgSpecBase *as) { // X is actually an (const X &) typedef typename type_traits::value_type value_type; - check_data (); + check_data (as); const value_type *r = ((const value_type *)mp_read); mp_read += item_size (); return *r; } template - X read_impl (const npod_cref_tag &, tl::Heap &) + X read_impl (const npod_cref_tag &, tl::Heap &, const ArgSpecBase *as) { // X is actually an (const X &) typedef typename type_traits::value_type value_type; - check_data (); + check_data (as); const value_type *r = *((const value_type **)mp_read); mp_read += item_size (); if (! r) { - throw NilPointerToReference (); + throw_nil_for_reference (as); } return *r; } template - X read_impl (const x_cref_tag &, tl::Heap &) + X read_impl (const x_cref_tag &, tl::Heap &, const ArgSpecBase *as) { // X is actually an (const X &) typedef typename type_traits::value_type value_type; - check_data (); + check_data (as); const value_type *r = *((const value_type **)mp_read); mp_read += item_size (); if (! r) { - throw NilPointerToReference (); + throw_nil_for_reference (as); } return *r; } template - X read_impl (const ptr_tag &, tl::Heap &) + X read_impl (const ptr_tag &, tl::Heap &, const ArgSpecBase *as) { // X is actually an (X *) typedef typename type_traits::value_type value_type; - check_data (); + check_data (as); value_type * const &r = *((value_type **)mp_read); mp_read += item_size (); return r; } template - X read_impl (const pod_cptr_tag &, tl::Heap &) + X read_impl (const pod_cptr_tag &, tl::Heap &, const ArgSpecBase *as) { // X is actually an (const X *) - check_data (); + check_data (as); bool h = *(bool *)mp_read; mp_read += item_size (); X r = h ? (X)mp_read : (X)0; @@ -474,29 +525,29 @@ private: // see notes on the serialization for this type: template - X read_impl (const npod_cptr_tag &, tl::Heap &) + X read_impl (const npod_cptr_tag &, tl::Heap &, const ArgSpecBase *as) { // X is actually an (const X *) - check_data (); + check_data (as); X r = *((X *)mp_read); mp_read += item_size (); return r; } template - X read_impl (const x_cptr_tag &, tl::Heap &) + X read_impl (const x_cptr_tag &, tl::Heap &, const ArgSpecBase *as) { // X is actually an (const X *) - check_data (); + check_data (as); X r = *((X *)mp_read); mp_read += item_size (); return r; } template - X read_impl (const adaptor_direct_tag &, tl::Heap &heap) + X read_impl (const adaptor_direct_tag &, tl::Heap &heap, const ArgSpecBase *as) { - check_data (); + check_data (as); std::unique_ptr p (*(AdaptorBase **)mp_read); mp_read += item_size (); @@ -508,11 +559,11 @@ private: } template - X read_impl (const adaptor_cref_tag &, tl::Heap &heap) + X read_impl (const adaptor_cref_tag &, tl::Heap &heap, const ArgSpecBase *as) { typedef typename tl::get_inner_type::result x_type; - check_data (); + check_data (as); std::unique_ptr p (*(AdaptorBase **)mp_read); mp_read += item_size (); @@ -526,11 +577,11 @@ private: } template - X read_impl (const adaptor_ref_tag &, tl::Heap &heap) + X read_impl (const adaptor_ref_tag &, tl::Heap &heap, const ArgSpecBase *as) { typedef typename tl::get_inner_type::result x_type; - check_data (); + check_data (as); AdaptorBase *p = *(AdaptorBase **)mp_read; mp_read += item_size (); @@ -544,11 +595,11 @@ private: } template - X read_impl (const adaptor_cptr_tag &, tl::Heap &heap) + X read_impl (const adaptor_cptr_tag &, tl::Heap &heap, const ArgSpecBase *as) { typedef typename tl::get_inner_type::result x_type; - check_data (); + check_data (as); std::unique_ptr p (*(AdaptorBase **)mp_read); mp_read += item_size (); @@ -564,11 +615,11 @@ private: } template - X read_impl (const adaptor_ptr_tag &, tl::Heap &heap) + X read_impl (const adaptor_ptr_tag &, tl::Heap &heap, const ArgSpecBase *as) { typedef typename tl::get_inner_type::result x_type; - check_data (); + check_data (as); AdaptorBase *p = *(AdaptorBase **)mp_read; mp_read += item_size (); diff --git a/src/lay/lay/FillDialog.ui b/src/lay/lay/FillDialog.ui index c523ef32b..f0853556c 100644 --- a/src/lay/lay/FillDialog.ui +++ b/src/lay/lay/FillDialog.ui @@ -1,694 +1,979 @@ - + + FillDialog - - + + 0 0 - 681 - 773 + 691 + 437 - + Fill Tool - - + + 9 - - 6 + + 9 - - - - Fill Area + + 9 + + + 9 + + + + + Qt::Horizontal - - - 9 - - - 6 - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + 0 + + + + Fill Area + + + + + + + 0 + 0 + - - 6 - - - - - Select how the region to fill is specified. - - - - All (whole cell) - - - - - Shapes on layer ... - - - - - Selected shapes - - - - - Single box with ... - - - - - Ruler bounding boxes - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - 0 - - - - - + + 0 - + + 0 + + + 0 + + + 0 + + 6 - - - - Qt::Vertical + + + + QFrame::NoFrame - - - 241 - 40 - + + QFrame::Raised - + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Select how the region to fill is specified. + + + + All (whole cell) + + + + + Shapes on layer ... + + + + + Selected shapes + + + + + Single box with ... + + + + + Ruler bounding boxes + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + - + - + Qt::Horizontal - + - 121 + 40 20 - - - - true + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Keep distance to border of fill area of + + + + + + + + 0 + 0 + + + + Leave empty for no distance. Otherwise enter a distance in micron (can be anisotropic in the form "dx,dy") + + + + + + + µm + + + + + + + + + + + + + 1 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 6 + + + + + Qt::Vertical + + + + 241 + 40 + + + + + + + + Qt::Horizontal + + + + 121 + 20 + + + + + + + + true + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 6 + + + + + true + + + Box Boundaries + + + + 9 + + + 9 + + + 9 + + + 9 + + + 6 + + + + + y = + + + + + + + 1st corner + + + + + + + + + + y = + + + + + + + + + + x = + + + + + + + + + + 2nd corner + + + + + + + + + + x = + + + + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Exclude Areas + + + + + + + 0 + 0 + + + + Leave empty for no distance. Otherwise enter a distance in micron (can be anisotropic in the form "dx,dy") + + + + + + + µm + + + + + + + Spacing around exclude areas + + + + + + + The fill will not be generated over the areas specified by these layers + + + + All layers + + + + + All visible layers + + + + + Selected layers + + + + + No exclude + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 15 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 309 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + Fill Cell + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 6 + + + + + µm (keep distance between fill cells unless stitched) + + + + + + + Fill cell margin + + + + + + + Qt::Horizontal + + + + 171 + 23 + + + + + + + + + 0 + 0 + + + + This layer defines the borders of the fill cell. The fill cells will be stiched seamlessly at this border + + + QComboBox::AdjustToContents + + + + + + + Select the cell which will be used as tiling cell for the fill area + + + + + + + (for aligning the fill cell and default tile raster) + + + + + + + Step vectors + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + + 0 + 0 + + + + Boundary layer + + + + + + + + 0 + 0 + + + + Fill cell + + + + + + + + 0 + 0 + + + + Leave empty for no distance. Otherwise enter a distance in micron (can be anisotropic in the form "dx,dy") + + + + + + + Qt::Horizontal + + + + 171 + 20 + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + Row + + + + + + + (dx, dy in µm) + + + + + + + (dx, dy in µm) + + + + + + + Column + + + + + + + By default, the step vectors are given by the bounding box width (row dx) and height (column dy) of the fill cell or the boundary layer - if one is given. + + + true + + + + + + + + + + + + + ... - - - - 0 - - + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + Options + + + + + + 6 - - - + + 0 + + + 0 + + + 0 + + + 0 + + + + + Enhanced fill (leave fixed raster to enhance fill of small regions) + + + + + + + Second-order fill cell for remaining regions + + true - - Box Boundaries + + false - - + + 9 - + + 9 + + + 9 + + + 9 + + 6 - - - - y = + + + + µm (keep distance between fill cells unless stitched) - - - - 2nd corner + + + + Specify the fill cell for the secondary fill analogous to the primary fill cell - - - - x = + + + + + 0 + 0 + + + + Leave empty for no distance. Otherwise enter a distance in micron (can be anisotropic in the form "dx,dy") - - - - 1st corner + + + + ... - - - - x = + + + + Qt::Horizontal + + + + 141 + 20 + + + + + + + + Fill cell margin - - - - y = + + + + + 0 + 0 + + + + Fill cell - - + + + + The second order fill cell is used to fill space remaining from the first fill step. Thus, the second order fill cell must be smaller than the first order fill cell. The boundary layer must be the same for the second order fill cell. + + + true + + - - + + + + Step vectors + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + - - - - - + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Row + + + + + + + (dx, dy in µm) + + + + + + + (dx, dy in µm) + + + + + + + By default, the step vectors are given by the bounding box width (row dx) and height (column dy) of the fill cell or the boundary layer - if one is given. + + + true + + + + + + + + + + Column + + + + + + + + - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 + + + + + Qt::Vertical - - 6 + + + 20 + 40 + - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 6 - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Keep distance to border of fill area of - - - - - - - - 5 - 0 - 0 - 0 - - - - Leave empty for no distance. Otherwise enter a distance in micron (can be anisotropic in the form "dx,dy") - - - - - - - µm - - - - - - - - - - - - - - - - Exclude Areas - - - - 9 - - - 6 - - - - - The fill will not be generated over the areas specified by these layers - - - - All layers - - - - - All visible layers - - - - - Selected layers - - - - - No exclude - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Spacing around exclude areas - - - - - - - - 5 - 0 - 0 - 0 - - - - Leave empty for no distance. Otherwise enter a distance in micron (can be anisotropic in the form "dx,dy") - - - - - - - µm - - - - - - - - - - Fill Cell - - - - 9 - - - 6 - - - - - Fill cell margin - - - - - - - - 5 - 5 - 0 - 0 - - - - This layer defines the borders of the fill cell. The fill cells will be stiched seamlessly at this border - - - QComboBox::AdjustToContents - - - - - - - - 5 - 0 - 0 - 0 - - - - Leave empty for no distance. Otherwise enter a distance in micron (can be anisotropic in the form "dx,dy") - - - - - - - µm - - - - - - - Qt::Horizontal - - - - 171 - 20 - - - - - - - - Qt::Horizontal - - - - 171 - 23 - - - - - - - - (controls tiling raster of the cells) - - - - - - - - 0 - 5 - 0 - 0 - - - - Boundary layer - - - - - - - ... - - - - - - - Select the cell which will be used as tiling cell for the fill area - - - - - - - - 1 - 5 - 0 - 0 - - - - Fill cell - - - - - - - - - - Options - - - - 9 - - - 6 - - - - - Enhanced fill (leave fixed raster to enhance fill of small regions) - - - - - - - Second-order fill cell for remaining regions - - - true - - - false - - - - 9 - - - 6 - - - - - - 1 - 5 - 0 - 0 - - - - Fill cell - - - - - - - Specify the fill cell for the secondary fill analogous to the primary fill cell - - - - - - - ... - - - - - - - Qt::Horizontal - - - - 141 - 20 - - - - - - - - µm - - - - - - - - 5 - 0 - 0 - 0 - - - - Leave empty for no distance. Otherwise enter a distance in micron (can be anisotropic in the form "dx,dy") - - - - - - - Fill cell margin - - - - - - - The second order fill cell is used to fill space remaining from the first fill step. Thus, the second order fill cell must be smaller than the first order fill cell. The boundary layer must be the same for the second order fill cell. - - - true - - - - - - - - - - - - - Qt::Vertical - - - - 623 - 20 - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok - + + + + @@ -701,12 +986,29 @@ + tabWidget + fill_area_cbx cb_layer le_x1 le_y1 le_x2 le_y2 - button_box + distance_le + layer_spec_cbx + exclude_le + fill_cell_le + choose_fc_pb + fill_margin_le + fc_boundary_layer + row_le + column_le + enhanced_cb + second_order_fill_cb + fill_cell_2nd_le + choose_fc_2nd_pb + fill2_margin_le + row_2nd_le + column_2nd_le @@ -716,11 +1018,11 @@ FillDialog accept() - + 248 254 - + 157 274 @@ -732,11 +1034,11 @@ FillDialog reject() - + 316 260 - + 286 274 diff --git a/src/lay/lay/doc/about/drc_ref_global.xml b/src/lay/lay/doc/about/drc_ref_global.xml index 5e1739598..d383f6227 100644 --- a/src/lay/lay/doc/about/drc_ref_global.xml +++ b/src/lay/lay/doc/about/drc_ref_global.xml @@ -683,6 +683,17 @@ The following example computes every part of the input which is closer than out = in.drc(primary & foreign.sized(0.5.um))

+

"global_transform" - Gets or sets a global transformation

+ +

Usage:

+
    +
  • global_transform
  • +
  • global_transform([ transformations ])
  • +
+

+Applies a global transformation to the default source layout. +See Source#global_transform for a description of this feature. +

"holes" - Selects all holes from the input polygons

Usage:

diff --git a/src/lay/lay/doc/about/drc_ref_layer.xml b/src/lay/lay/doc/about/drc_ref_layer.xml index b0bfbca96..0d2b34c84 100644 --- a/src/lay/lay/doc/about/drc_ref_layer.xml +++ b/src/lay/lay/doc/about/drc_ref_layer.xml @@ -1023,6 +1023,111 @@ The following images show the effect of the extents method:

+

"fill" - Fills the region with regular pattern of shapes

+ +

Usage:

+
    +
  • layer.fill([ options ])
  • +
+

+This method will attempt to fill the polygons of the layer with a regular pattern +of shapes. +

+The fill function currently is not available in deep mode. +

+Options are: +

    +
  • hstep(x) or hstep(x, y) : specifies the horizontal step pitch of the pattern. x must be +a positive value. A vertical displacement component can be specified too, which results in a skewed pattern.
  • +
  • vstep(y) or vstep(x, y) : specifies the vertical step pitch of the pattern. y must be +a positive value. A horizontal displacement component can be specified too, which results in a skewed pattern.
  • +
  • origin(x, y) : specifies a fixed point to align the pattern with. This point specifies the location +of the reference point for one pattern cell.
  • +
  • auto_origin : lets the algorithm choose the origin. This may result is a slightly better fill coverage +as the algorithm is able to determine a pattern origin per fill island.
  • +
  • fill_pattern(..) : specifies the fill pattern.
  • +
+

+"fill_pattern" generates a fill pattern object. This object is used for configuring the fill pattern +content. Fill pattern need to be named. The name will be used for generating the fill cell. +

+To provide a fill pattern, create a fill pattern object and add shapes to it. The following example creates +a fill pattern named "FILL_CELL" and adds a 1x1 micron box on layer 1/0: +

+

+p = fill_pattern("FILL_CELL")
+p.shape(1, 0, box(0.0, 0.0, 1.0, 1.0))
+
+

+See box for details about the box specification. You can also add paths or polygons with path or polygon. +

+A more compact way of writing this is: +

+

+p = fill_pattern("FILL_CELL").shape(1, 0, box(0.0, 0.0, 1.0, 1.0))
+
+

+The fill pattern can be given a reference point which is used for placing the pattern. The reference point +is the one which is aligned with the pattern origin. The following code will assign (-0.5, -0.5) as the reference +point for the 1x1 micron rectangle. Hence the reference point is a little below and left of the rectangle which +in turn shifts the rectangle fill pattern to the right and up: +

+

+p = fill_pattern("FILL_CELL")
+p.shape(1, 0, box(0.0, 0.0, 1.0, 1.0))
+p.origin(-0.5, -0.5)
+
+

+Without a reference point given, the lower left corner of the fill pattern's bounding box will be used +as the reference point. The reference point will also defined the footprint of the fill cell - more precisely +the lower left corner. When step vectors are given, the fill cell's footprint is taken to be a rectangle +having the horizontal and vertical step pitch for width and height respectively. This way the fill cells +will be arrange seamlessly. However, the cell's dimensions can be changed, so that the fill cells +can overlap or there is a space between the cells. To change the dimensions use the "dim" method. +

+The following example specifies a fill cell with an active area of -0.5 .. 1.5 in both directions +(2 micron width and height). With these dimensions the fill cell's footprint is independent of the +step pitch: +

+

+p = fill_pattern("FILL_CELL")
+p.shape(1, 0, box(0.0, 0.0, 1.0, 1.0))
+p.origin(-0.5, -0.5)
+p.dim(2.0, 2.0)
+
+

+With these ingredients will can use the fill function. The first example fills the polygons +of "to_fill" with an orthogonal pattern of 1x1 micron rectangles with a pitch of 2 microns: +

+

+pattern = fill_pattern("FILL_CELL").shape(1, 0, box(0.0, 0.0, 1.0, 1.0)).origin(-0.5, -0.5)
+to_fill.fill(pattern, hstep(2.0), vstep(2.0))
+
+

+This second example will create a skewed fill pattern in auto-origin mode: +

+

+pattern = fill_pattern("FILL_CELL").shape(1, 0, box(0.0, 0.0, 1.0, 1.0)).origin(-0.5, -0.5)
+to_fill.fill(pattern, hstep(2.0, 1.0), vstep(-1.0, 2.0), auto_origin)
+
+

+The fill function can only work with a target layout for output. +It will not work for report output. +

+The layers generated by the fill cells is only available for input later in the +script if the output layout is identical to the input layouts. +If you need the area missed by the fill function, try fill_with_left. +

+

"fill_with_left" - Fills the region with regular pattern of shapes

+ +

Usage:

+
    +
  • layer.fill_with_left([ options ])
  • +
+

+This method has the same call syntax and functionality than fill. Other than this method +it will return the area not covered by fill cells as a DRC layer. +

"first_edges" - Returns the first edges of an edge pair collection

Usage:

diff --git a/src/lay/lay/doc/about/drc_ref_source.xml b/src/lay/lay/doc/about/drc_ref_source.xml index 7b29857d1..05525f41e 100644 --- a/src/lay/lay/doc/about/drc_ref_source.xml +++ b/src/lay/lay/doc/about/drc_ref_source.xml @@ -109,6 +109,40 @@ The extent function is useful to invert a layer: inverse_1 = extent.sized(100.0) - input(1, 0)

+

"global_transform" - Gets or sets a global transformation

+ +

Usage:

+
    +
  • global_transform
  • +
  • global_transform([ transformations ])
  • +
+

+This method returns a new source representing the transformed layout. It is provided in the spritit of +Source#clip and similar methods. +

+The transformation +is either given as a DTrans, DVector or DCplxTrans object or as one of the +following specifications: +

+

    +
  • "shift(x, y)": shifts the input layout horizontally by x and vertically by y micrometers
  • +
  • "rotate(a)": rotates the input layout by a degree counter-clockwise
  • +
  • "magnify(m)": magnifies the input layout by the factor m (NOTE: using fractional scale factors may result in small gaps due to grid snapping)
  • +
  • "mirror_x": mirrors the input layout at the x axis
  • +
  • "mirror_y": mirrors the input layout at the y axis
  • +
+

+Multiple transformation specs can be given. In that case the transformations are applied right to left. +Using "global_transform" will reset any global transformation present already. +Without an argument, the global transformation is reset. +

+The following example rotates the layout by 90 degree at the origin (0, 0) and then shifts it up by +100 micrometers: +

+

+source.global_transform(shift(0, 100.um), rotate(90.0))
+
+

"input" - Specifies input from a source

Usage:

diff --git a/src/lay/lay/layFillDialog.cc b/src/lay/lay/layFillDialog.cc index dff0a42e2..4d59325ca 100644 --- a/src/lay/lay/layFillDialog.cc +++ b/src/lay/lay/layFillDialog.cc @@ -31,8 +31,8 @@ #include "antService.h" #include "tlException.h" #include "tlString.h" -#include "layMainWindow.h" #include "tlExceptions.h" +#include "layMainWindow.h" #include "layCellSelectionForm.h" #include "edtService.h" @@ -83,6 +83,8 @@ FillDialog::FillDialog (lay::Dispatcher *main, lay::LayoutView *view) Ui::FillDialog::setupUi (this); + fc_boundary_layer->set_no_layer_available (true); + fill_area_stack->setCurrentIndex (0); connect (fill_area_cbx, SIGNAL (currentIndexChanged (int)), this, SLOT (fill_area_changed (int))); connect (button_box, SIGNAL (accepted ()), this, SLOT (ok_pressed ())); @@ -132,66 +134,184 @@ FillDialog::choose_fc_2nd () } } -static void -collect_fill_regions (const db::Layout &layout, - db::cell_index_type cell_index, - unsigned int layer, - const db::CplxTrans &trans, - std::vector ®ions) +void +FillDialog::generate_fill (const FillParameters &fp) { - const db::Cell &cell = layout.cell (cell_index); - if (! cell.bbox (layer).empty ()) { - - // any shapes to consider .. - for (db::ShapeIterator sh = cell.shapes (layer).begin (db::ShapeIterator::Polygons | db::ShapeIterator::Paths | db::ShapeIterator::Boxes); ! sh.at_end (); ++sh) { - regions.push_back (db::Polygon ()); - sh->polygon (regions.back ()); - } - - for (db::Cell::const_iterator inst = cell.begin (); ! inst.at_end (); ++inst) { - for (db::CellInstArray::iterator a = inst->cell_inst ().begin (); ! a.at_end (); ++a) { - collect_fill_regions (layout, inst->cell_index (), layer, trans * inst->cell_inst ().complex_trans (*a), regions); - } - } - - } -} - -void -collect_fill_regions (const db::Layout &layout, - db::cell_index_type cell_index, - unsigned int layer, - std::vector ®ions) -{ - collect_fill_regions (layout, cell_index, layer, db::CplxTrans (), regions); -} - -void -FillDialog::ok_pressed () -{ -BEGIN_PROTECTED - if (tl::verbosity () >= 10) { tl::info << "Running fill"; } lay::CellView cv = mp_view->cellview (mp_view->active_cellview_index ()); + db::Layout &ly = cv->layout (); std::vector exclude_layers; - - if (layer_spec_cbx->currentIndex () == 0) { + if (fp.exclude_all_layers) { // all layers - for (db::Layout::layer_iterator l = cv->layout ().begin_layers (); l != cv->layout ().end_layers (); ++l) { + for (db::Layout::layer_iterator l = ly.begin_layers (); l != ly.end_layers (); ++l) { exclude_layers.push_back ((*l).first); } + } else { + // some layers + for (std::vector::const_iterator l = fp.exclude_layers.begin (); l != fp.exclude_layers.end (); ++l) { + exclude_layers.push_back (ly.get_layer (*l)); + } + } + + bool enhanced_fill = enhanced_cb->isChecked (); + + db::Coord exclude_x = db::coord_traits::rounded (fp.exclude_distance.x () / ly.dbu ()); + db::Coord exclude_y = db::coord_traits::rounded (fp.exclude_distance.y () / ly.dbu ()); + + db::Coord distance_x = db::coord_traits::rounded (fp.border_distance.x () / ly.dbu ()); + db::Coord distance_y = db::coord_traits::rounded (fp.border_distance.y () / ly.dbu ()); + + db::Vector fill_margin = db::CplxTrans (ly.dbu ()).inverted () * fp.fill_cell_margin; + db::Vector fill_margin2 = db::CplxTrans (ly.dbu ()).inverted () * fp.fill_cell_margin2; + + std::pair fc = cv->layout ().cell_by_name (fp.fill_cell_name.c_str ()); + if (! fc.first) { + throw tl::Exception (tl::to_string (QObject::tr ("Fill cell not found: ")) + fp.fill_cell_name); + } + + const db::Cell *fill_cell = &ly.cell (fc.second); + + const db::Cell *fill_cell2 = 0; + if (! fp.fill_cell_name2.empty ()) { + std::pair fc2 = cv->layout ().cell_by_name (fp.fill_cell_name2.c_str ()); + if (! fc2.first) { + throw tl::Exception (tl::to_string (QObject::tr ("Secondary fill cell not found: ")) + fp.fill_cell_name2); + } + fill_cell2 = &ly.cell (fc2.second); + } + + db::Vector row_step = db::CplxTrans (ly.dbu ()).inverted () * fp.row_step; + db::Vector column_step = db::CplxTrans (ly.dbu ()).inverted () * fp.column_step; + db::Box fc_bbox = db::CplxTrans (ly.dbu ()).inverted () * fp.fc_bbox; + + db::Vector row_step2 = db::CplxTrans (ly.dbu ()).inverted () * fp.row_step2; + db::Vector column_step2 = db::CplxTrans (ly.dbu ()).inverted () * fp.column_step2; + db::Box fc_bbox2 = db::CplxTrans (ly.dbu ()).inverted () * fp.fc_bbox2; + + + if (tl::verbosity () >= 20) { + tl::info << "Collecting fill regions"; + } + + mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Fill"))); + + db::Region fill_region; + if (fp.fill_region_mode == FillParameters::Region) { + fill_region = fp.fill_region; + } else if (fp.fill_region_mode == FillParameters::WholeCell) { + fill_region.insert (cv->layout ().cell (cv.cell_index ()).bbox ()); + } else if (fp.fill_region_mode == FillParameters::Layer) { + unsigned int layer_index = cv->layout ().get_layer (fp.fill_region_layer); + fill_region = db::Region (db::RecursiveShapeIterator (cv->layout (), *cv.cell (), layer_index)); + } + + fill_region.enable_progress (tl::to_string (tr ("Computing fill region"))); + + if (! fill_region.empty ()) { + + db::EdgeProcessor ep; + + if (tl::verbosity () >= 20) { + tl::info << "Preprocessing fill regions"; + } + + // preprocess fill regions + if (distance_x != 0 || distance_y != 0) { + fill_region.size (distance_x, distance_y); + } else { + fill_region.merge (); + } + + db::Box fr_bbox = fill_region.bbox (); + + if (tl::verbosity () >= 20) { + tl::info << "Collecting exclude areas"; + } + + // collect sized shapes from the exclude layers + db::Region es; + es.enable_progress (tl::to_string (tr ("Preparing exclude layers"))); + for (std::vector ::const_iterator l = exclude_layers.begin (); l != exclude_layers.end (); ++l) { + + db::Region exclude (db::RecursiveShapeIterator (cv->layout (), *cv.cell (), *l)); + exclude.enable_progress (tl::to_string (tr ("Preparing exclude layer: ")) + cv->layout ().get_properties (*l).to_string ()); + + if (exclude_x != 0 || exclude_y != 0) { + exclude.size (exclude_x, exclude_y); + } else { + exclude.merge (); + } + + es += exclude; + + } + + if (tl::verbosity () >= 20) { + tl::info << "Computing effective fill region"; + } + + // Perform the NOT operation to create the fill region + fill_region -= es; + + db::Region new_fill_area; + + int step = 0; + + do { + + ++step; + + if (tl::verbosity () >= 20) { + tl::info << "Major iteration (primary/secondary fill cell)"; + } + + if (! enhanced_fill) { + db::fill_region (cv.cell (), fill_region, fill_cell->cell_index (), fc_bbox, row_step, column_step, fr_bbox.p1 (), false, fill_cell2 ? &fill_region : 0, fill_margin, fill_cell2 ? &fill_region : 0); + } else { + db::fill_region_repeat (cv.cell (), fill_region, fill_cell->cell_index (), fc_bbox, row_step, column_step, fill_margin, fill_cell2 ? &fill_region : 0); + } + + fill_cell = fill_cell2; + row_step = row_step2; + column_step = column_step2; + fc_bbox = fc_bbox2; + fill_margin = fill_margin2; + + fill_cell2 = 0; + + } while (fill_cell != 0 && ! fill_region.empty ()); + + } + + if (tl::verbosity () >= 20) { + tl::info << "Fill done"; + } +} + +FillParameters +FillDialog::get_fill_parameters () +{ + FillParameters fp; + + lay::CellView cv = mp_view->cellview (mp_view->active_cellview_index ()); + + fp.exclude_all_layers = false; + + if (layer_spec_cbx->currentIndex () == 0) { + + fp.exclude_all_layers = true; } else if (layer_spec_cbx->currentIndex () == 1) { // visible layers for (lay::LayerPropertiesConstIterator l = mp_view->begin_layers (); ! l.at_end (); ++l) { if (! l->has_children () && l->visible (true)) { - exclude_layers.push_back (l->layer_index ()); + fp.exclude_layers.push_back (cv->layout ().get_properties (l->layer_index ())); } } @@ -201,7 +321,7 @@ BEGIN_PROTECTED std::vector s = mp_view->selected_layers (); for (std::vector::const_iterator l = s.begin (); l != s.end (); ++l) { if (! (*l)->has_children ()) { - exclude_layers.push_back ((*l)->layer_index ()); + fp.exclude_layers.push_back (cv->layout ().get_properties ((*l)->layer_index ())); } } @@ -219,100 +339,14 @@ BEGIN_PROTECTED } } - db::Coord exclude_x = db::coord_traits::rounded (x / cv->layout ().dbu ()); - db::Coord exclude_y = db::coord_traits::rounded (y / cv->layout ().dbu ()); - - // read distance to border - x = 0.0, y = 0.0; - s = tl::to_string (distance_le->text ()); - ex = tl::Extractor (s.c_str ()); - if (ex.try_read (x)) { - if (ex.test (",") && ex.try_read (y)) { - // take x, y - } else { - y = x; - } - } - - db::Coord distance_x = db::coord_traits::rounded (x / cv->layout ().dbu ()); - db::Coord distance_y = db::coord_traits::rounded (y / cv->layout ().dbu ()); - - // read fill cell margin - db::Vector fill_margin (exclude_x, exclude_y); - x = 0.0, y = 0.0; - s = tl::to_string (fill_margin_le->text ()); - ex = tl::Extractor (s.c_str ()); - if (ex.try_read (x)) { - if (ex.test (",") && ex.try_read (y)) { - // take x, y - } else { - y = x; - } - fill_margin = db::Vector (db::coord_traits::rounded (x / cv->layout ().dbu ()), db::coord_traits::rounded (y / cv->layout ().dbu ())); - } - - // read fill cell 2 margin - db::Vector fill2_margin (exclude_x, exclude_y); - x = 0.0, y = 0.0; - s = tl::to_string (fill2_margin_le->text ()); - ex = tl::Extractor (s.c_str ()); - if (ex.try_read (x)) { - if (ex.test (",") && ex.try_read (y)) { - // take x, y - } else { - y = x; - } - fill2_margin = db::Vector (db::coord_traits::rounded (x / cv->layout ().dbu ()), db::coord_traits::rounded (y / cv->layout ().dbu ())); - } - - // get the fill cell - std::pair fc = cv->layout ().cell_by_name (tl::to_string (fill_cell_le->text ()).c_str ()); - if (! fc.first) { - throw tl::Exception (tl::to_string (QObject::tr ("Fill cell not found: ")) + tl::to_string (fill_cell_le->text ())); - } - - const db::Cell *fill_cell = &cv->layout ().cell (fc.second); - - int fc_bbox_layer = fc_boundary_layer->current_layer (); - if (fc_bbox_layer < 0 || ! cv->layout ().is_valid_layer (fc_bbox_layer)) { - throw tl::Exception (tl::to_string (QObject::tr ("No valid layer selected to get fill cell's bounding box from"))); - } - - db::Box fc_bbox = fill_cell->bbox (fc_bbox_layer); - if (fc_bbox.empty ()) { - throw tl::Exception (tl::to_string (QObject::tr ("No valid layer selected to get fill cell's bounding box from - layer is empty for the fill cell"))); - } - - bool enhanced_fill = enhanced_cb->isChecked (); - - const db::Cell *fill_cell2 = 0; - db::Box fc_bbox2; - - if (second_order_fill_cb->isChecked ()) { - - std::pair fc = cv->layout ().cell_by_name (tl::to_string (fill_cell_2nd_le->text ()).c_str ()); - if (! fc.first) { - throw tl::Exception (tl::to_string (QObject::tr ("Second order fill cell not found: ")) + tl::to_string (fill_cell_2nd_le->text ())); - } - - fill_cell2 = &cv->layout ().cell (fc.second); - - fc_bbox2 = fill_cell2->bbox (fc_bbox_layer); - if (fc_bbox2.empty ()) { - throw tl::Exception (tl::to_string (QObject::tr ("Second order fill cell is empty for the given boundary layer"))); - } - - } - - if (tl::verbosity () >= 20) { - tl::info << "Collecting fill regions"; - } + fp.exclude_distance = db::DVector (x, y); // get the fill regions - std::vector fill_regions; if (fill_area_cbx->currentIndex () == 3) { + fp.fill_region_mode = FillParameters::Region; + // explicit fill box if (le_x1->text ().isEmpty () || le_x2->text ().isEmpty () || @@ -327,23 +361,27 @@ BEGIN_PROTECTED tl::from_string (tl::to_string (le_y1->text ()), y1); tl::from_string (tl::to_string (le_y2->text ()), y2); - fill_regions.push_back (db::Polygon (db::Box (db::DBox (db::DPoint (x1, y1), db::DPoint (x2, y2)) * (1.0 / cv->layout ().dbu ())))); + fp.fill_region.insert (db::Box (db::DBox (db::DPoint (x1, y1), db::DPoint (x2, y2)) * (1.0 / cv->layout ().dbu ()))); } else if (fill_area_cbx->currentIndex () == 4) { + fp.fill_region_mode = FillParameters::Region; + // ruler ant::Service *ant_service = mp_view->get_plugin (); if (ant_service) { ant::AnnotationIterator ant = ant_service->begin_annotations (); while (! ant.at_end ()) { - fill_regions.push_back (db::Polygon (db::Box (db::DBox (ant->p1 (), ant->p2 ()) * (1.0 / cv->layout ().dbu ())))); + fp.fill_region.insert (db::Box (db::DBox (ant->p1 (), ant->p2 ()) * (1.0 / cv->layout ().dbu ()))); ++ant; } } } else if (fill_area_cbx->currentIndex () == 1) { + fp.fill_region_mode = FillParameters::Layer; + // specified layer int sel_layer = cb_layer->current_layer (); @@ -351,174 +389,174 @@ BEGIN_PROTECTED throw tl::Exception (tl::to_string (QObject::tr ("No valid layer selected to get fill regions from"))); } - collect_fill_regions (cv->layout (), cv.cell_index (), (unsigned int) sel_layer, fill_regions); + fp.fill_region_layer = cv->layout ().get_properties (sel_layer); } else if (fill_area_cbx->currentIndex () == 0) { - // whole cell - fill_regions.push_back (db::Polygon (cv.cell ()->bbox ())); + fp.fill_region_mode = FillParameters::WholeCell; } else if (fill_area_cbx->currentIndex () == 2) { + fp.fill_region_mode = FillParameters::Region; + // selection std::vector edt_services = mp_view->get_plugins (); for (std::vector::const_iterator s = edt_services.begin (); s != edt_services.end (); ++s) { for (edt::Service::objects::const_iterator sel = (*s)->selection ().begin (); sel != (*s)->selection ().end (); ++sel) { if (! sel->is_cell_inst () && (sel->shape ().is_polygon () || sel->shape ().is_path () || sel->shape ().is_box ())) { - fill_regions.push_back (db::Polygon ()); - sel->shape ().polygon (fill_regions.back ()); + db::Polygon poly; + sel->shape ().polygon (poly); + fp.fill_region.insert (poly); } } } } + // read distance to border + x = 0.0, y = 0.0; + s = tl::to_string (distance_le->text ()); + ex = tl::Extractor (s.c_str ()); + if (ex.try_read (x)) { + if (ex.test (",") && ex.try_read (y)) { + // take x, y + } else { + y = x; + } + } + + fp.border_distance = db::DVector (x, y); + + // read fill cell margin + x = 0.0, y = 0.0; + s = tl::to_string (fill_margin_le->text ()); + ex = tl::Extractor (s.c_str ()); + if (ex.try_read (x)) { + if (ex.test (",") && ex.try_read (y)) { + // take x, y + } else { + y = x; + } + } + + fp.fill_cell_margin = db::DVector (x, y); + + // read fill cell 2 margin + x = 0.0, y = 0.0; + s = tl::to_string (fill2_margin_le->text ()); + ex = tl::Extractor (s.c_str ()); + if (ex.try_read (x)) { + if (ex.test (",") && ex.try_read (y)) { + // take x, y + } else { + y = x; + } + } + + fp.fill_cell_margin2 = db::DVector (x, y); + + fp.fill_cell_name = tl::to_string (fill_cell_le->text ()); + + // get the fill cell + std::pair fc = cv->layout ().cell_by_name (fp.fill_cell_name.c_str ()); + if (! fc.first) { + throw tl::Exception (tl::to_string (QObject::tr ("Fill cell not found: ")) + tl::to_string (fill_cell_le->text ())); + } + + const db::Cell *fill_cell = &cv->layout ().cell (fc.second); + + int fc_bbox_layer = fc_boundary_layer->current_layer (); + if (fc_bbox_layer >= 0 && ! cv->layout ().is_valid_layer (fc_bbox_layer)) { + throw tl::Exception (tl::to_string (QObject::tr ("No valid layer selected to get fill cell's bounding box from"))); + } + + fp.enhanced_fill = enhanced_cb->isChecked (); + + db::DBox fc_bbox = db::CplxTrans (cv->layout ().dbu ()) * (fc_bbox_layer < 0 ? fill_cell->bbox () : fill_cell->bbox (fc_bbox_layer)); + if (fc_bbox.empty ()) { + if (fc_bbox_layer >= 0) { + throw tl::Exception (tl::to_string (QObject::tr ("No valid layer selected to get fill cell's bounding box from - layer is empty for the fill cell"))); + } else { + throw tl::Exception (tl::to_string (QObject::tr ("Fill cell is empty"))); + } + } + + s = tl::to_string (row_le->text ()); + ex = tl::Extractor (s.c_str ()); + if (ex.try_read (x) && ex.test (",") && ex.try_read (y)) { + fp.row_step = db::DVector (x, y); + } else { + fp.row_step = db::DVector (fc_bbox.width (), 0.0); + } + + s = tl::to_string (column_le->text ()); + ex = tl::Extractor (s.c_str ()); + if (ex.try_read (x) && ex.test (",") && ex.try_read (y)) { + fp.column_step = db::DVector (x, y); + } else { + fp.column_step = db::DVector (0.0, fc_bbox.height ()); + } + + fp.fc_bbox = fc_bbox; + + if (second_order_fill_cb->isChecked ()) { + + db::DBox fc_bbox2; + + fp.fill_cell_name2 = tl::to_string (fill_cell_2nd_le->text ()); + + std::pair fc = cv->layout ().cell_by_name (fp.fill_cell_name2.c_str ()); + if (! fc.first) { + throw tl::Exception (tl::to_string (QObject::tr ("Second order fill cell not found: ")) + tl::to_string (fill_cell_2nd_le->text ())); + } + + const db::Cell *fill_cell2 = &cv->layout ().cell (fc.second); + + fc_bbox2 = db::CplxTrans (cv->layout ().dbu ()) * (fc_bbox_layer < 0 ? fill_cell2->bbox () : fill_cell2->bbox (fc_bbox_layer)); + if (fc_bbox2.empty ()) { + throw tl::Exception (tl::to_string (QObject::tr ("Second order fill cell is empty for the given boundary layer"))); + } + + s = tl::to_string (row_2nd_le->text ()); + ex = tl::Extractor (s.c_str ()); + if (ex.try_read (x) && ex.test (",") && ex.try_read (y)) { + fp.row_step2 = db::DVector (x, y); + } else { + fp.row_step2 = db::DVector (fc_bbox2.width (), 0.0); + } + + s = tl::to_string (column_2nd_le->text ()); + ex = tl::Extractor (s.c_str ()); + if (ex.try_read (x) && ex.test (",") && ex.try_read (y)) { + fp.column_step2 = db::DVector (x, y); + } else { + fp.column_step2 = db::DVector (0.0, fc_bbox2.height ()); + } + + fp.fc_bbox2 = fc_bbox2; + + } + + return fp; +} + +void +FillDialog::ok_pressed () +{ +BEGIN_PROTECTED + + FillParameters fp = get_fill_parameters (); + mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Fill"))); - if (! fill_regions.empty ()) { - - db::EdgeProcessor ep; - - if (tl::verbosity () >= 20) { - tl::info << "Preprocessing fill regions"; - } - - // TODO: progress - - // preprocess fill regions - if (distance_x != 0 || distance_y != 0) { - - std::vector fp; - ep.enable_progress (tl::to_string (QObject::tr ("Preparing fill regions"))); - ep.size (fill_regions, -distance_x, -distance_y, fp, 2 /*mode*/, false /*=don't resolve holes*/); - ep.disable_progress (); - - fill_regions.swap (fp); - - } - - std::sort (fill_regions.begin (), fill_regions.end ()); - fill_regions.erase (std::unique (fill_regions.begin (), fill_regions.end ()), fill_regions.end ()); - - // determine the fill region's bbox for selectively getting the exclude shapes - db::Box fr_bbox; - for (std::vector ::const_iterator fr = fill_regions.begin (); fr != fill_regions.end (); ++fr) { - fr_bbox += fr->box (); - } - - if (tl::verbosity () >= 20) { - tl::info << "Collecting exclude areas"; - } - - // collect sized shapes from the exclude layers - std::vector es; - for (std::vector ::const_iterator l = exclude_layers.begin (); l != exclude_layers.end (); ++l) { - - std::vector shapes; - - size_t n = 0; - for (db::RecursiveShapeIterator si (cv->layout (), *cv.cell (), *l); ! si.at_end (); ++si) { - if (si->is_polygon () || si->is_path () || si->is_box ()) { - ++n; - } - } - - shapes.reserve (n); - - for (db::RecursiveShapeIterator si (cv->layout (), *cv.cell (), *l); ! si.at_end (); ++si) { - if (si->is_polygon () || si->is_path () || si->is_box ()) { - shapes.push_back (db::Polygon ()); - si->polygon (shapes.back ()); - shapes.back ().transform (si.trans ()); - } - } - - ep.enable_progress (tl::to_string (QObject::tr ("Preparing exclude regions"))); - ep.size (shapes, exclude_x, exclude_y, es, 2 /*mode*/, false /*=don't resolve holes*/); - ep.disable_progress (); - - } - - if (tl::verbosity () >= 20) { - tl::info << "Computing effective fill region"; - } - - // Perform the NOT operation to create the fill region - std::vector fill_area; - ep.enable_progress (tl::to_string (QObject::tr ("Computing fill region"))); - ep.boolean (fill_regions, es, fill_area, db::BooleanOp::ANotB, false /*=don't resolve holes*/); - ep.disable_progress (); - - std::vector new_fill_area; - - int step = 0; - - do { - - ++step; - - if (tl::verbosity () >= 20) { - tl::info << "Major iteration (primary/secondary fill cell)"; - } - - std::vector non_filled_area; - - int iteration = 0; - - do { - - ++iteration; - - if (tl::verbosity () >= 20 && enhanced_fill) { - tl::info << "Minor iteration (enhanced fill)"; - } - - tl::RelativeProgress progress (tl::sprintf (tl::to_string (QObject::tr ("Fill iteration %d (%s fill step)")), iteration, step == 1 ? tl::to_string (QObject::tr ("primary")) : tl::to_string (QObject::tr ("secondary"))), fill_area.size (), 10); - - new_fill_area.clear (); - - for (std::vector ::const_iterator fp0 = fill_area.begin (); fp0 != fill_area.end (); ++fp0) { - - if (tl::verbosity () >= 30) { - tl::info << "Compute fill for one region :" << fp0->to_string (); - } - - bool any_fill = fill_region (cv.cell (), *fp0, fill_cell->cell_index (), fc_bbox, fr_bbox.p1 (), enhanced_fill, (enhanced_fill || fill_cell2) ? &new_fill_area : 0, fill_margin); - if (! any_fill) { - non_filled_area.push_back (*fp0); - } - - ++progress; - - } - - fill_area.swap (new_fill_area); - - } while (enhanced_fill && ! fill_area.empty ()); - - if (fill_area.empty ()) { - fill_area.swap (non_filled_area); - } else if (fill_cell2) { - fill_area.insert (fill_area.end (), non_filled_area.begin (), non_filled_area.end ()); - } - - fill_cell = fill_cell2; - fc_bbox = fc_bbox2; - fill_margin = fill2_margin; - - fill_cell2 = 0; - fc_bbox2 = db::Box (); - - } while (fill_cell != 0 && ! fill_area.empty ()); - + try { + generate_fill (fp); + mp_view->manager ()->commit (); + } catch (...) { + mp_view->manager ()->cancel (); + throw; } - if (tl::verbosity () >= 20) { - tl::info << "Fill done"; - } - - mp_view->manager ()->commit (); - // close this dialog QDialog::accept (); diff --git a/src/lay/lay/layFillDialog.h b/src/lay/lay/layFillDialog.h index 689912dff..620aeb9e9 100644 --- a/src/lay/lay/layFillDialog.h +++ b/src/lay/lay/layFillDialog.h @@ -29,13 +29,46 @@ #include "layLayoutView.h" #include "layPlugin.h" #include "layMarker.h" +#include "layCommon.h" + +#include "dbRegion.h" #include namespace lay { -class FillDialog +struct LAY_PUBLIC FillParameters +{ + FillParameters () + : exclude_all_layers (true), fill_region_mode (WholeCell), enhanced_fill (false) + { } + + enum FillRegionMode { + WholeCell, + Region, + Layer + }; + + bool exclude_all_layers; + std::vector exclude_layers; + FillRegionMode fill_region_mode; + db::Region fill_region; + db::LayerProperties fill_region_layer; + db::DVector exclude_distance; + db::DVector border_distance; + bool enhanced_fill; + std::string fill_cell_name; + db::DVector fill_cell_margin; + db::DVector row_step, column_step; + db::DBox fc_bbox; + std::string fill_cell_name2; + db::DVector fill_cell_margin2; + db::DVector row_step2, column_step2; + db::DBox fc_bbox2; +}; + +class LAY_PUBLIC FillDialog : public QDialog, public lay::Plugin, private Ui::FillDialog @@ -59,6 +92,9 @@ private: // implementation of the lay::Plugin interface void menu_activated (const std::string &symbol); + void generate_fill (const FillParameters &fp); + FillParameters get_fill_parameters (); + lay::LayoutView *mp_view; }; diff --git a/src/lay/lay/layProgressWidget.cc b/src/lay/lay/layProgressWidget.cc index bd0f711f4..9c16f2f63 100644 --- a/src/lay/lay/layProgressWidget.cc +++ b/src/lay/lay/layProgressWidget.cc @@ -338,7 +338,11 @@ ProgressWidget::set_progress (tl::Progress *progress) double v = progress->value (); pb->set_value (v, value); - progress = progress->next (); + if (progress->final ()) { + progress = 0; + } else { + progress = progress->next (); + } } else { pb->hide (); diff --git a/src/plugins/streamers/dxf/db_plugin/dbDXFReader.h b/src/plugins/streamers/dxf/db_plugin/dbDXFReader.h index 7aca5f607..5268e18e2 100644 --- a/src/plugins/streamers/dxf/db_plugin/dbDXFReader.h +++ b/src/plugins/streamers/dxf/db_plugin/dbDXFReader.h @@ -43,7 +43,8 @@ namespace db { -class Matrix3d; +template class matrix_3d; +typedef matrix_3d Matrix3d; /** * @brief Generic base class of DXF reader exceptions diff --git a/src/plugins/streamers/pcb/db_plugin/dbRS274XReader.cc b/src/plugins/streamers/pcb/db_plugin/dbRS274XReader.cc index 12f9a626b..bf333635a 100644 --- a/src/plugins/streamers/pcb/db_plugin/dbRS274XReader.cc +++ b/src/plugins/streamers/pcb/db_plugin/dbRS274XReader.cc @@ -513,6 +513,7 @@ RS274XReader::do_read () if (m_polygon_mode) { // D02 strokes close the polygon (and restart a new one) + if (m_polygon_points.size () >= 3) { db::DPolygon poly; poly.assign_hull (m_polygon_points.begin (), m_polygon_points.end ()); @@ -520,6 +521,7 @@ RS274XReader::do_read () } m_polygon_points.clear (); + m_polygon_points.push_back (db::DPoint (x, y)); } diff --git a/src/plugins/streamers/pcb/unit_tests/dbGerberImport.cc b/src/plugins/streamers/pcb/unit_tests/dbGerberImport.cc index fc7e6bb10..33b7bde5d 100644 --- a/src/plugins/streamers/pcb/unit_tests/dbGerberImport.cc +++ b/src/plugins/streamers/pcb/unit_tests/dbGerberImport.cc @@ -297,6 +297,11 @@ TEST(26) run_test (_this, "pos-neg"); } +TEST(27) +{ + run_test (_this, "polygon-mode"); +} + TEST(X2_1) { run_test (_this, "x2-1"); diff --git a/src/rdb/rdb/rdbUtils.cc b/src/rdb/rdb/rdbUtils.cc index 931da36c0..336ed963e 100644 --- a/src/rdb/rdb/rdbUtils.cc +++ b/src/rdb/rdb/rdbUtils.cc @@ -146,7 +146,7 @@ public: m_cell_stack.pop_back (); } - virtual new_inst_mode new_inst (const db::RecursiveShapeIterator * /*iter*/, const db::CellInstArray &inst, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) + virtual new_inst_mode new_inst (const db::RecursiveShapeIterator * /*iter*/, const db::CellInstArray &inst, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { db::cell_index_type ci = inst.object ().cell_index (); if (m_id_to_cell.find (ci) != m_id_to_cell.end ()) { @@ -156,7 +156,7 @@ public: } } - virtual void shape (const db::RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) + virtual void shape (const db::RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans & /*always_apply*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) { tl_assert (! m_cell_stack.empty ()); create_item_from_shape (mp_rdb, m_cell_stack.back ()->id (), mp_cat->id (), m_trans, shape); @@ -209,9 +209,9 @@ public: } } - virtual void shape (const db::RecursiveShapeIterator *iter, const db::Shape &shape, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) + virtual void shape (const db::RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans & /*always_apply*/, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) { - create_item_from_shape (mp_rdb, mp_rdb_cell->id (), mp_cat->id (), m_trans * iter->trans (), shape); + create_item_from_shape (mp_rdb, mp_rdb_cell->id (), mp_cat->id (), m_trans * trans, shape); } public: diff --git a/src/tl/tl/tlExpression.cc b/src/tl/tl/tlExpression.cc index ee7d5a031..a00dbefd1 100644 --- a/src/tl/tl/tlExpression.cc +++ b/src/tl/tl/tlExpression.cc @@ -104,7 +104,7 @@ ExpressionParserContext::where () const int line = 1; size_t col = 0; - for (size_t p = 0; p < len; ++p, ++col) { + for (size_t p = 0; p < pos; ++p) { if (text [p] == '\n') { ++line; col = 1; @@ -2044,7 +2044,7 @@ public: throw EvalError (tl::sprintf (tl::to_string (tr ("Not a valid object for a method call (not an object) - value is %s")), v->to_parsable_string ()), m_context); } } else { - throw EvalError (tl::sprintf (tl::to_string (tr ("Not a valid object for a method call (wrong type) - value is %1")), v->to_parsable_string ()), m_context); + throw EvalError (tl::sprintf (tl::to_string (tr ("Not a valid object for a method call (wrong type) - value is %s")), v->to_parsable_string ()), m_context); } tl::Variant o; diff --git a/src/tl/tl/tlProgress.cc b/src/tl/tl/tlProgress.cc index 1315ad65e..331f42461 100644 --- a/src/tl/tl/tlProgress.cc +++ b/src/tl/tl/tlProgress.cc @@ -138,7 +138,7 @@ const double yield_timeout = 0.3; const size_t default_yield_interval = 1000; Progress::Progress (const std::string &desc, size_t yield_interval, bool can_cancel) - : m_desc (desc), m_title (desc), + : m_desc (desc), m_title (desc), m_final (false), m_interval_count (0), m_yield_interval (yield_interval == 0 ? default_yield_interval : yield_interval), m_last_value (-1.0), diff --git a/src/tl/tl/tlProgress.h b/src/tl/tl/tlProgress.h index 13dce6791..87ee7cdfa 100644 --- a/src/tl/tl/tlProgress.h +++ b/src/tl/tl/tlProgress.h @@ -250,6 +250,25 @@ public: return m_desc; } + /** + * @brief Sets a value indicating whether the progress is a "final" one + * + * A final progress will prevent child progress objects from showing. It basically summarizes child operations. + * By default, a progress object is not final. + */ + void set_final (bool f) + { + m_final = f; + } + + /** + * @brief Gets a value indicating whether the progress is a "final" one + */ + bool final () const + { + return m_final; + } + /** * @brief Render the title string */ @@ -296,6 +315,7 @@ private: std::string m_desc, m_last_desc; std::string m_title; + bool m_final; size_t m_interval_count; size_t m_yield_interval; double m_last_value; diff --git a/src/tl/tl/tlThreadedWorkers.cc b/src/tl/tl/tlThreadedWorkers.cc index 48ddbabd0..01f31ce7d 100644 --- a/src/tl/tl/tlThreadedWorkers.cc +++ b/src/tl/tl/tlThreadedWorkers.cc @@ -159,6 +159,16 @@ TaskList::put_front (Task *task) } } +size_t +TaskList::size () const +{ + size_t n = 0; + for (Task *t = mp_first; t; t = t->mp_next) { + ++n; + } + return n; +} + // ----------------------------------------------------------------------------- // tl::JobBase implementation @@ -264,6 +274,11 @@ JobBase::start () mp_workers.back ()->start (this, int (mp_workers.size ()) - 1); } + while (m_nworkers < int (mp_workers.size ())) { + delete mp_workers.back (); + mp_workers.pop_back (); + } + for (int i = 0; i < int (mp_workers.size ()); ++i) { setup_worker (mp_workers [i]); mp_workers [i]->reset_stop_request (); @@ -278,39 +293,59 @@ JobBase::start () std::unique_ptr sync_worker (create_worker ()); setup_worker (sync_worker.get ()); - while (! m_task_list.is_empty ()) { - std::unique_ptr task (m_task_list.fetch ()); - try { - sync_worker->perform_task (task.get ()); - } catch (TaskTerminatedException) { - // Stop the thread. - break; - } catch (WorkerTerminatedException) { - // Stop the thread. - break; - } catch (tl::Exception &ex) { - log_error (ex.msg ()); - } catch (std::exception &ex) { - log_error (ex.what ()); - } catch (...) { - log_error (tl::to_string (tr ("Unspecific error"))); - } - } - - // clean up any remaining tasks - while (! m_task_list.is_empty ()) { - Task *task = m_task_list.fetch (); - if (task) { - delete task; + try { + + while (! m_task_list.is_empty ()) { + + std::unique_ptr task (m_task_list.fetch ()); + before_sync_task (task.get ()); + + try { + sync_worker->perform_task (task.get ()); + } catch (TaskTerminatedException) { + // Stop the thread. + break; + } catch (WorkerTerminatedException) { + // Stop the thread. + break; + } catch (tl::Exception &ex) { + log_error (ex.msg ()); + } catch (std::exception &ex) { + log_error (ex.what ()); + } catch (...) { + log_error (tl::to_string (tr ("Unspecific error"))); + } + + after_sync_task (task.get ()); + } + + } catch (...) { + // handle exceptions raised by before_sync_task or after_sync_task + cleanup (); + m_running = false; + throw; } + cleanup (); finished (); m_running = false; } } +void +JobBase::cleanup () +{ + // clean up any remaining tasks + while (! m_task_list.is_empty ()) { + Task *task = m_task_list.fetch (); + if (task) { + delete task; + } + } +} + bool JobBase::is_running () { diff --git a/src/tl/tl/tlThreadedWorkers.h b/src/tl/tl/tlThreadedWorkers.h index 48eab9dbc..172ea8b02 100644 --- a/src/tl/tl/tlThreadedWorkers.h +++ b/src/tl/tl/tlThreadedWorkers.h @@ -109,6 +109,11 @@ public: return mp_first; } + /** + * @brief Gets the number of tasks + */ + size_t size () const; + private: Task *mp_first, *mp_last; @@ -163,6 +168,14 @@ public: */ void schedule (Task *task); + /** + * @brief Gets the number of tasks in the queue + */ + size_t tasks () const + { + return m_task_list.size (); + } + /** * @brief Start the execution of the job */ @@ -227,6 +240,16 @@ protected: */ virtual void setup_worker (Worker * /*worker*/) { } + /** + * @brief This method is called before the given task is started in sync mode (workers == 0) + */ + virtual void before_sync_task (Task * /*task*/) { } + + /** + * @brief This method is called after the given task has finished in sync mode (workers == 0) + */ + virtual void after_sync_task (Task * /*task*/) { } + /** * @brief Indicates that the job has finished * @@ -276,6 +299,7 @@ private: Task *get_task (int for_worker); void log_error (const std::string &s); + void cleanup (); }; /** diff --git a/testdata/algo/fill_tool1.gds b/testdata/algo/fill_tool1.gds new file mode 100644 index 000000000..0bcaec0c4 Binary files /dev/null and b/testdata/algo/fill_tool1.gds differ diff --git a/testdata/algo/fill_tool2.gds b/testdata/algo/fill_tool2.gds new file mode 100644 index 000000000..c2e221473 Binary files /dev/null and b/testdata/algo/fill_tool2.gds differ diff --git a/testdata/algo/fill_tool3.gds b/testdata/algo/fill_tool3.gds new file mode 100644 index 000000000..d2470efa0 Binary files /dev/null and b/testdata/algo/fill_tool3.gds differ diff --git a/testdata/algo/fill_tool4.gds b/testdata/algo/fill_tool4.gds new file mode 100644 index 000000000..580215174 Binary files /dev/null and b/testdata/algo/fill_tool4.gds differ diff --git a/testdata/algo/fill_tool_au1.gds b/testdata/algo/fill_tool_au1.gds new file mode 100644 index 000000000..1cd62ae2c Binary files /dev/null and b/testdata/algo/fill_tool_au1.gds differ diff --git a/testdata/algo/fill_tool_au2.gds b/testdata/algo/fill_tool_au2.gds new file mode 100644 index 000000000..a28cfd4c1 Binary files /dev/null and b/testdata/algo/fill_tool_au2.gds differ diff --git a/testdata/algo/fill_tool_au3.gds b/testdata/algo/fill_tool_au3.gds new file mode 100644 index 000000000..9fa4c2739 Binary files /dev/null and b/testdata/algo/fill_tool_au3.gds differ diff --git a/testdata/algo/fill_tool_au3a.gds b/testdata/algo/fill_tool_au3a.gds new file mode 100644 index 000000000..e67a6a2ec Binary files /dev/null and b/testdata/algo/fill_tool_au3a.gds differ diff --git a/testdata/algo/fill_tool_au3b.gds b/testdata/algo/fill_tool_au3b.gds new file mode 100644 index 000000000..878de1226 Binary files /dev/null and b/testdata/algo/fill_tool_au3b.gds differ diff --git a/testdata/algo/fill_tool_au3c.gds b/testdata/algo/fill_tool_au3c.gds new file mode 100644 index 000000000..0c44b3f6f Binary files /dev/null and b/testdata/algo/fill_tool_au3c.gds differ diff --git a/testdata/algo/fill_tool_au4.gds b/testdata/algo/fill_tool_au4.gds new file mode 100644 index 000000000..63a8af8ee Binary files /dev/null and b/testdata/algo/fill_tool_au4.gds differ diff --git a/testdata/algo/fill_tool_au4b.gds b/testdata/algo/fill_tool_au4b.gds new file mode 100644 index 000000000..9bb6c2c82 Binary files /dev/null and b/testdata/algo/fill_tool_au4b.gds differ diff --git a/testdata/algo/fill_tool_au4c.gds b/testdata/algo/fill_tool_au4c.gds new file mode 100644 index 000000000..5fab766ce Binary files /dev/null and b/testdata/algo/fill_tool_au4c.gds differ diff --git a/testdata/drc/drcSimpleTests_31.drc b/testdata/drc/drcSimpleTests_31.drc new file mode 100644 index 000000000..6f3480b6a --- /dev/null +++ b/testdata/drc/drcSimpleTests_31.drc @@ -0,0 +1,57 @@ + +source $drc_test_source +target $drc_test_target + +if $drc_test_deep + deep +end + +def self_test(id, a, b) + a == b || raise(id + ": self-test failed (" + a.inspect + " != " + b.inspect + ")") +end + +self_test("magnify(2.0)", source.global_transformation.to_s, "r0 *1 0,0") + +global_transform(magnify(2.0)) + +self_test("magnify(2.0)", source.global_transformation.to_s, "r0 *2 0,0") + +global_transform(magnify(2.0), rotate(90.0)) + +self_test("magnify(2.0)", source.global_transformation.to_s, "r90 *2 0,0") + +global_transform(mirror_x, mirror_y) + +self_test("magnify(2.0)", source.global_transformation.to_s, "r180 *1 0,0") + +global_transform(magnify(2.0), shift(10.um, 20.um)) + +self_test("magnify(2.0)", source.global_transformation.to_s, "r0 *2 10,20") + + +# The actual DRC test + +l1 = input(1, 0) +l2 = input(2, 0) + +l1.output(1, 0) +l2.output(2, 0) + +l1.merged.output(10, 0) +l1.sized(100.nm).output(11, 0) +l2.sized(100.nm).output(12, 0) + +# reset +global_transform + +l1 = input(1, 0) +l2 = input(2, 0) + + +l1.output(101, 0) +l2.output(102, 0) + +l1.merged.output(110, 0) +l1.sized(100.nm).output(111, 0) +l2.sized(100.nm).output(112, 0) + diff --git a/testdata/drc/drcSimpleTests_31.gds b/testdata/drc/drcSimpleTests_31.gds new file mode 100644 index 000000000..735cbfe7c Binary files /dev/null and b/testdata/drc/drcSimpleTests_31.gds differ diff --git a/testdata/drc/drcSimpleTests_32.drc b/testdata/drc/drcSimpleTests_32.drc new file mode 100644 index 000000000..a6660946c --- /dev/null +++ b/testdata/drc/drcSimpleTests_32.drc @@ -0,0 +1,59 @@ + +source $drc_test_source +target $drc_test_target + +clip(0.um, 0.um, 26.um, 45.um) + +if $drc_test_deep + deep +end + +def self_test(id, a, b) + a == b || raise(id + ": self-test failed (" + a.inspect + " != " + b.inspect + ")") +end + +self_test("magnify(2.0)", source.global_transformation.to_s, "r0 *1 0,0") + +global_transform(magnify(2.0)) + +self_test("magnify(2.0)", source.global_transformation.to_s, "r0 *2 0,0") + +global_transform(magnify(2.0), rotate(90.0)) + +self_test("magnify(2.0)", source.global_transformation.to_s, "r90 *2 0,0") + +global_transform(mirror_x, mirror_y) + +self_test("magnify(2.0)", source.global_transformation.to_s, "r180 *1 0,0") + +global_transform(magnify(2.0), shift(10.um, 20.um)) + +self_test("magnify(2.0)", source.global_transformation.to_s, "r0 *2 10,20") + + +# The actual DRC test + +l1 = input(1, 0) +l2 = input(2, 0) + +l1.output(1, 0) +l2.output(2, 0) + +l1.merged.output(10, 0) +l1.sized(100.nm).output(11, 0) +l2.sized(100.nm).output(12, 0) + +# reset +global_transform + +l1 = input(1, 0) +l2 = input(2, 0) + + +l1.output(101, 0) +l2.output(102, 0) + +l1.merged.output(110, 0) +l1.sized(100.nm).output(111, 0) +l2.sized(100.nm).output(112, 0) + diff --git a/testdata/drc/drcSimpleTests_32.gds b/testdata/drc/drcSimpleTests_32.gds new file mode 100644 index 000000000..735cbfe7c Binary files /dev/null and b/testdata/drc/drcSimpleTests_32.gds differ diff --git a/testdata/drc/drcSimpleTests_33.drc b/testdata/drc/drcSimpleTests_33.drc new file mode 100644 index 000000000..3e16fbb64 --- /dev/null +++ b/testdata/drc/drcSimpleTests_33.drc @@ -0,0 +1,34 @@ + +source $drc_test_source +target $drc_test_target + +tiles(10.um) + +global_transform(magnify(2.0), shift(10.um, 20.um)) + +# The actual DRC test + +l1 = input(1, 0) +l2 = input(2, 0) + +l1.output(1, 0) +l2.output(2, 0) + +l1.merged.output(10, 0) +l1.sized(100.nm).output(11, 0) +l2.sized(100.nm).output(12, 0) + +# reset +global_transform + +l1 = input(1, 0) +l2 = input(2, 0) + + +l1.output(101, 0) +l2.output(102, 0) + +l1.merged.output(110, 0) +l1.sized(100.nm).output(111, 0) +l2.sized(100.nm).output(112, 0) + diff --git a/testdata/drc/drcSimpleTests_33.gds b/testdata/drc/drcSimpleTests_33.gds new file mode 100644 index 000000000..735cbfe7c Binary files /dev/null and b/testdata/drc/drcSimpleTests_33.gds differ diff --git a/testdata/drc/drcSimpleTests_40.drc b/testdata/drc/drcSimpleTests_40.drc new file mode 100644 index 000000000..de7f810f1 --- /dev/null +++ b/testdata/drc/drcSimpleTests_40.drc @@ -0,0 +1,33 @@ + +source $drc_test_source +target $drc_test_target + +if $drc_test_deep + deep +end + +l1 = input(1, 0) + +f1 = l1 +f2 = extent - l1.sized(1.0) + +p1 = fill_pattern("PAT1").shape(100, 0, box(0, 0, 1.um, 1.um)).origin(-0.5.um, -0.5.um) +p2 = fill_pattern("PAT2").shape(100, 1, box(0, 0, 1.um, 1.um)).origin(-0.5.um, -0.5.um) +p3 = fill_pattern("PAT3").shape(100, 2, box(0, 0, 1.um, 1.um)).shape(1000, 0, box(-0.5.um, -0.5.um, 1.5.um, 1.5.um)) + +p11 = fill_pattern("PAT11").shape(101, 0, box(0, 0, 1.um, 1.um)).origin(-0.5.um, -0.5.um) +p12 = fill_pattern("PAT12").shape(101, 1, box(0, 0, 1.um, 1.um)).origin(-0.5.um, -0.5.um) +p13 = fill_pattern("PAT13").shape(101, 2, box(0, 0, 1.um, 1.um)).shape(1000, 0, box(-0.5.um, -0.5.um, 1.5.um, 1.5.um)) + +f1.fill(p1, hstep(2.0, 1.0), vstep(-1.0, 2.0)) +f1.fill(p2, hstep(2.0, 1.0), vstep(-1.0, 2.0), auto_origin) +f1.fill(p3) + +f2.fill(p11, hstep(2.0, 1.0), vstep(-1.0, 2.0)) +f2.fill(p12, hstep(2.0, 1.0), vstep(-1.0, 2.0), auto_origin) +f2.fill(p13) + +l1.output(1, 0) +f1.output(10, 0) +f2.output(11, 0) + diff --git a/testdata/drc/drcSimpleTests_40.gds b/testdata/drc/drcSimpleTests_40.gds new file mode 100644 index 000000000..e94b5b8f3 Binary files /dev/null and b/testdata/drc/drcSimpleTests_40.gds differ diff --git a/testdata/drc/drcSimpleTests_41.drc b/testdata/drc/drcSimpleTests_41.drc new file mode 100644 index 000000000..b0af087fa --- /dev/null +++ b/testdata/drc/drcSimpleTests_41.drc @@ -0,0 +1,35 @@ + +source $drc_test_source +target $drc_test_target + +if $drc_test_deep + deep +end + +tiles(100, 100) + +l1 = input(1, 0) + +f1 = l1 +f2 = extent - l1.sized(1.0) + +p1 = fill_pattern("PAT1").shape(100, 0, box(0, 0, 1.um, 1.um)).origin(-0.5.um, -0.5.um) +p2 = fill_pattern("PAT2").shape(100, 1, box(0, 0, 1.um, 1.um)).origin(-0.5.um, -0.5.um) +p3 = fill_pattern("PAT3").shape(100, 2, box(0, 0, 1.um, 1.um)).shape(1000, 0, box(-0.5.um, -0.5.um, 1.5.um, 1.5.um)) + +p11 = fill_pattern("PAT11").shape(101, 0, box(0, 0, 1.um, 1.um)).origin(-0.5.um, -0.5.um) +p12 = fill_pattern("PAT12").shape(101, 1, box(0, 0, 1.um, 1.um)).origin(-0.5.um, -0.5.um) +p13 = fill_pattern("PAT13").shape(101, 2, box(0, 0, 1.um, 1.um)).shape(1000, 0, box(-0.5.um, -0.5.um, 1.5.um, 1.5.um)) + +f1.fill(p1, hstep(2.0, 1.0), vstep(-1.0, 2.0)) +f1.fill(p2, hstep(2.0, 1.0), vstep(-1.0, 2.0), auto_origin) +f1.fill(p3) + +f2.fill(p11, hstep(2.0, 1.0), vstep(-1.0, 2.0)) +f2.fill(p12, hstep(2.0, 1.0), vstep(-1.0, 2.0), auto_origin) +f2.fill(p13) + +l1.output(1, 0) +f1.output(10, 0) +f2.output(11, 0) + diff --git a/testdata/drc/drcSimpleTests_41.gds b/testdata/drc/drcSimpleTests_41.gds new file mode 100644 index 000000000..e94b5b8f3 Binary files /dev/null and b/testdata/drc/drcSimpleTests_41.gds differ diff --git a/testdata/drc/drcSimpleTests_42.drc b/testdata/drc/drcSimpleTests_42.drc new file mode 100644 index 000000000..fa9bce1fb --- /dev/null +++ b/testdata/drc/drcSimpleTests_42.drc @@ -0,0 +1,19 @@ + +source $drc_test_source +target $drc_test_target + +if $drc_test_deep + deep +end + +l1 = input(1, 0) + +f1 = l1 + +p1 = fill_pattern("PAT1").shape(100, 0, box(0, 0, 1.um, 1.um)).origin(-0.5.um, -0.5.um) + +f1.fill_with_left(p1, hstep(2.0, 1.0), vstep(-1.0, 2.0)).output(100, 0) + +l1.output(1, 0) +f1.output(10, 0) + diff --git a/testdata/drc/drcSimpleTests_42.gds b/testdata/drc/drcSimpleTests_42.gds new file mode 100644 index 000000000..e9e05712b Binary files /dev/null and b/testdata/drc/drcSimpleTests_42.gds differ diff --git a/testdata/drc/drcSimpleTests_43.drc b/testdata/drc/drcSimpleTests_43.drc new file mode 100644 index 000000000..732a68ed4 --- /dev/null +++ b/testdata/drc/drcSimpleTests_43.drc @@ -0,0 +1,21 @@ + +source $drc_test_source +target $drc_test_target + +if $drc_test_deep + deep +end + +tiles(5, 5) + +l1 = input(1, 0) + +f1 = l1 + +p1 = fill_pattern("PAT1").shape(100, 0, box(0, 0, 1.um, 1.um)).origin(-0.5.um, -0.5.um) + +f1.fill_with_left(p1, hstep(2.0, 1.0), vstep(-1.0, 2.0)).output(100, 0) + +l1.output(1, 0) +f1.output(10, 0) + diff --git a/testdata/drc/drcSimpleTests_43.gds b/testdata/drc/drcSimpleTests_43.gds new file mode 100644 index 000000000..e9e05712b Binary files /dev/null and b/testdata/drc/drcSimpleTests_43.gds differ diff --git a/testdata/drc/drcSimpleTests_44.drc b/testdata/drc/drcSimpleTests_44.drc new file mode 100644 index 000000000..34d4e997a --- /dev/null +++ b/testdata/drc/drcSimpleTests_44.drc @@ -0,0 +1,19 @@ + +source $drc_test_source +target $drc_test_target + +if $drc_test_deep + deep +end + +l1 = input(1, 0) + +f1 = l1 + +p1 = fill_pattern("PAT1").shape(100, 0, box(0, 0, 1.um, 1.um)).origin(-1.um, -1.um).dim(3.um, 3.um) + +f1.fill_with_left(p1, hstep(2.0, 1.0), vstep(-1.0, 2.0)).output(100, 0) + +l1.output(1, 0) +f1.output(10, 0) + diff --git a/testdata/drc/drcSimpleTests_44.gds b/testdata/drc/drcSimpleTests_44.gds new file mode 100644 index 000000000..e9e05712b Binary files /dev/null and b/testdata/drc/drcSimpleTests_44.gds differ diff --git a/testdata/drc/drcSimpleTests_45.drc b/testdata/drc/drcSimpleTests_45.drc new file mode 100644 index 000000000..910f0c971 --- /dev/null +++ b/testdata/drc/drcSimpleTests_45.drc @@ -0,0 +1,21 @@ + +source $drc_test_source +target $drc_test_target + +if $drc_test_deep + deep +end + +tiles(5, 5) + +l1 = input(1, 0) + +f1 = l1 + +p1 = fill_pattern("PAT1").shape(100, 0, box(0, 0, 1.um, 1.um)).origin(-1.um, -1.um).dim(3.um, 3.um) + +f1.fill_with_left(p1, hstep(2.0, 1.0), vstep(-1.0, 2.0)).output(100, 0) + +l1.output(1, 0) +f1.output(10, 0) + diff --git a/testdata/drc/drcSimpleTests_45.gds b/testdata/drc/drcSimpleTests_45.gds new file mode 100644 index 000000000..e9e05712b Binary files /dev/null and b/testdata/drc/drcSimpleTests_45.gds differ diff --git a/testdata/drc/drcSimpleTests_au31.gds b/testdata/drc/drcSimpleTests_au31.gds new file mode 100644 index 000000000..9a33e4995 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au31.gds differ diff --git a/testdata/drc/drcSimpleTests_au31d.gds b/testdata/drc/drcSimpleTests_au31d.gds new file mode 100644 index 000000000..38514972a Binary files /dev/null and b/testdata/drc/drcSimpleTests_au31d.gds differ diff --git a/testdata/drc/drcSimpleTests_au32.gds b/testdata/drc/drcSimpleTests_au32.gds new file mode 100644 index 000000000..6b9829294 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au32.gds differ diff --git a/testdata/drc/drcSimpleTests_au32d.gds b/testdata/drc/drcSimpleTests_au32d.gds new file mode 100644 index 000000000..c899c06e9 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au32d.gds differ diff --git a/testdata/drc/drcSimpleTests_au33d.gds b/testdata/drc/drcSimpleTests_au33d.gds new file mode 100644 index 000000000..f3023fae4 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au33d.gds differ diff --git a/testdata/drc/drcSimpleTests_au40.gds b/testdata/drc/drcSimpleTests_au40.gds new file mode 100644 index 000000000..04a9f564c Binary files /dev/null and b/testdata/drc/drcSimpleTests_au40.gds differ diff --git a/testdata/drc/drcSimpleTests_au41.gds b/testdata/drc/drcSimpleTests_au41.gds new file mode 100644 index 000000000..f509aa097 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au41.gds differ diff --git a/testdata/drc/drcSimpleTests_au42.gds b/testdata/drc/drcSimpleTests_au42.gds new file mode 100644 index 000000000..01f2a2f5a Binary files /dev/null and b/testdata/drc/drcSimpleTests_au42.gds differ diff --git a/testdata/drc/drcSimpleTests_au43.gds b/testdata/drc/drcSimpleTests_au43.gds new file mode 100644 index 000000000..9684dc210 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au43.gds differ diff --git a/testdata/drc/drcSimpleTests_au44.gds b/testdata/drc/drcSimpleTests_au44.gds new file mode 100644 index 000000000..f47f36f18 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au44.gds differ diff --git a/testdata/drc/drcSimpleTests_au45.gds b/testdata/drc/drcSimpleTests_au45.gds new file mode 100644 index 000000000..7c45acceb Binary files /dev/null and b/testdata/drc/drcSimpleTests_au45.gds differ diff --git a/testdata/ruby/dbMatrix.rb b/testdata/ruby/dbMatrix.rb index 3cfe41d64..a0c52e34e 100644 --- a/testdata/ruby/dbMatrix.rb +++ b/testdata/ruby/dbMatrix.rb @@ -67,6 +67,48 @@ class DBMatrix_TestClass < TestBase end + def test_1i + + m = RBA::IMatrix2d.new + assert_equal("(1,0) (0,1)", m.to_s) + m = RBA::IMatrix2d.new(2.0) + assert_equal("(2,0) (0,2)", m.to_s) + m = RBA::IMatrix2d.newc(2, 90, true) + assert_equal("(0,2) (2,0)", m.to_s) + m = RBA::IMatrix2d.new(RBA::ICplxTrans.new(2, 90, true, RBA::Point.new(0, 0))) + assert_equal("(0,2) (2,0)", m.to_s) + m = RBA::IMatrix2d.newc(0.0, 2, 2, 90, true) + assert_equal("(0,2) (2,0)", m.to_s) + m = RBA::IMatrix2d.newc(17.0, 2, 3, 90, true) + assert_equal("17.000000", "%.6f" % m.shear_angle) + assert_equal("90.0", m.angle.to_s) + assert_equal("2.000000", "%.6f" % m.mag_x) + assert_equal("3.000000", "%.6f" % m.mag_y) + assert_equal(true, m.is_mirror?) + m = RBA::IMatrix2d.new(1, 2, 3, 4) + assert_equal("(1,2) (3,4)", m.to_s) + assert_equal("1.0", m.m11.to_s) + assert_equal("2.0", m.m12.to_s) + assert_equal("3.0", m.m21.to_s) + assert_equal("4.0", m.m22.to_s) + assert_equal("4.0", m.m(1,1).to_s) + assert_equal("2.0", m.m(0,1).to_s) + m = RBA::IMatrix2d.newc(2, 90, true) + assert_equal(true, m.is_mirror?) + t = m.cplx_trans + assert_equal("m45 *2 0,0", t.to_s) + m = RBA::IMatrix2d.newc(2, 90, false) + assert_equal("90.0", m.angle.to_s) + assert_equal(false, m.is_mirror?) + t = m.inverted.cplx_trans + assert_equal("r270 *0.5 0,0", t.to_s) + p = m.trans(RBA::Point.new(1, 2)) + assert_equal("-4,2", p.to_s) + assert_equal("(1,0) (0,1)", (m.inverted*m).to_s) + assert_equal("(0,-1.5) (1.5,0)", (m+m.inverted).to_s) + + end + def test_2 m = RBA::Matrix3d.new @@ -127,6 +169,66 @@ class DBMatrix_TestClass < TestBase end + def test_2i + + m = RBA::IMatrix3d.new + assert_equal("(1,0,0) (0,1,0) (0,0,1)", m.to_s) + m = RBA::IMatrix3d.new(2.0) + assert_equal("(2,0,0) (0,2,0) (0,0,1)", m.to_s) + m = RBA::IMatrix3d.newc(2, 90, true) + assert_equal("(0,2,0) (2,0,0) (0,0,1)", m.to_s) + m = RBA::IMatrix3d.new(RBA::ICplxTrans.new(2, 90, true, RBA::Point.new(1, 2))) + assert_equal("(0,2,1) (2,0,2) (0,0,1)", m.to_s) + m = RBA::IMatrix3d.newc(0.0, 2, 3, 90, true) + assert_equal("(0,3,0) (2,0,0) (0,0,1)", m.to_s) + m = RBA::IMatrix3d.newc(17.0, 2, 3, 90, true) + assert_equal("17.000000", "%.6f" % m.shear_angle) + assert_equal("90.0", m.angle.to_s) + assert_equal("2.000000", "%.6f" % m.mag_x) + assert_equal("3.000000", "%.6f" % m.mag_y) + assert_equal(true, m.is_mirror?) + m = RBA::IMatrix3d.newc(RBA::Point.new(1, 2), 17.0, 2, 3, 90, true) + assert_equal("17.000000", "%.6f" % m.shear_angle) + assert_equal("90.0", m.angle.to_s) + assert_equal("2.000000", "%.6f" % m.mag_x) + assert_equal("3.000000", "%.6f" % m.mag_y) + assert_equal("1,2", m.disp.to_s) + assert_equal(true, m.is_mirror?) + m = RBA::IMatrix3d.new(1, 2, 3, 4) + assert_equal("(1,2,0) (3,4,0) (0,0,1)", m.to_s) + assert_equal("4.0", m.m(1,1).to_s) + assert_equal("2.0", m.m(0,1).to_s) + assert_equal("0.0", m.m(0,2).to_s) + assert_equal("1.0", m.m(2,2).to_s) + m = RBA::IMatrix3d.newc(2, 90, true) + assert_equal(true, m.is_mirror?) + t = m.cplx_trans + assert_equal("m45 *2 0,0", t.to_s) + m = RBA::IMatrix3d.newc(2, 90, false) + assert_equal("90.0", m.angle.to_s) + assert_equal(false, m.is_mirror?) + t = m.inverted.cplx_trans + assert_equal("r270 *0.5 0,0", t.to_s) + p = m.trans(RBA::Point.new(1, 2)) + assert_equal("-4,2", p.to_s) + assert_equal("(1,0,0) (0,1,0) (0,0,1)", (m.inverted*m).to_s) + assert_equal("(0,-1.5,0) (1.5,0,0) (0,0,2)", (m+m.inverted).to_s) + m = RBA::IMatrix3d.new(0, 1, -1, 0, 1, 2) + t = m.cplx_trans + assert_equal("r270 *1 1,2", t.to_s) + m = RBA::IMatrix3d.new(0, 1, 1, -1, 0, 2, 0, 0, 1) + t = m.cplx_trans + assert_equal("r270 *1 1,2", t.to_s) + assert_equal("1", m.disp.x.to_s) + assert_equal("2", m.disp.y.to_s) + m = RBA::IMatrix3d.newc(0.1, -0.2, 1.0, RBA::Point.new(1, 2), 17.0, 2, 2, 270, true) + assert_equal("0.100000", "%.6f" % m.tx(1.0)) + assert_equal("-0.200000", "%.6f" % m.ty(1.0)) + assert_equal("17.000000", "%.6f" % m.shear_angle) + assert_equal("1,2", m.disp.to_s) + + end + def test_3 p = [ RBA::DPoint.new(1, 1), RBA::DPoint.new(2, 1), RBA::DPoint.new(2, 2) ] @@ -142,6 +244,66 @@ class DBMatrix_TestClass < TestBase end + def test_4 + + m = RBA::IMatrix2d::new(1.0, 0.5, -0.5, 2.0) + assert_equal((m * RBA::Point::new(10, 20)).to_s, "20,35") + assert_equal((m * RBA::Vector::new(10, 20)).to_s, "20,35") + assert_equal((m * RBA::Box::new(-5, -10, 10, 20)).to_s, "(-10,-25;20,43)") + assert_equal((m * RBA::Polygon::new(RBA::Box::new(-5, -10, 10, 20))).to_s, "(5,-25;-10,-18;5,43;20,35)") + assert_equal((m * RBA::SimplePolygon::new(RBA::Box::new(-5, -10, 10, 20))).to_s, "(5,-25;-10,-18;5,43;20,35)") + assert_equal((m * RBA::Edge::new(RBA::Point::new(-5, -10), RBA::Point::new(10, 20))).to_s, "(-10,-18;20,35)") + assert_equal(RBA::Region::new(RBA::Box::new(-5, -10, 10, 20)).transformed(m).to_s, "(5,-25;-10,-18;5,43;20,35)") + r = RBA::Region::new(RBA::Box::new(-5, -10, 10, 20)) + r.transform(m) + assert_equal(r.to_s, "(5,-25;-10,-18;5,43;20,35)") + assert_equal(RBA::Edges::new(RBA::Edge::new(RBA::Point::new(-5, -10), RBA::Point::new(10, 20))).transformed(m).to_s, "(-10,-18;20,35)") + r = RBA::Edges::new(RBA::Edge::new(RBA::Point::new(-5, -10), RBA::Point::new(10, 20))) + r.transform(m) + assert_equal(r.to_s, "(-10,-18;20,35)") + assert_equal(RBA::EdgePairs::new(RBA::EdgePair::new(RBA::Edge::new(RBA::Point::new(-5, -10), RBA::Point::new(10, 20)), RBA::Edge::new(RBA::Point::new(-6, -10), RBA::Point::new(11, 20)))).transformed(m).to_s, "(-10,-18;20,35)/(-11,-17;21,35)") + r = RBA::EdgePairs::new(RBA::EdgePair::new(RBA::Edge::new(RBA::Point::new(-5, -10), RBA::Point::new(10, 20)), RBA::Edge::new(RBA::Point::new(-6, -10), RBA::Point::new(11, 20)))) + r.transform(m) + assert_equal(r.to_s, "(-10,-18;20,35)/(-11,-17;21,35)") + + m = RBA::Matrix2d::new(1.0, 0.5, -0.5, 2.0) + assert_equal((m * RBA::DPoint::new(10, 20)).to_s, "20,35") + assert_equal((m * RBA::DVector::new(10, 20)).to_s, "20,35") + assert_equal((m * RBA::DBox::new(-5, -10, 10, 20)).to_s, "(-10,-25;20,42.5)") + assert_equal((m * RBA::DPolygon::new(RBA::DBox::new(-5, -10, 10, 20))).to_s, "(5,-25;-10,-17.5;5,42.5;20,35)") + assert_equal((m * RBA::DSimplePolygon::new(RBA::DBox::new(-5, -10, 10, 20))).to_s, "(5,-25;-10,-17.5;5,42.5;20,35)") + assert_equal((m * RBA::DEdge::new(RBA::DPoint::new(-5, -10), RBA::DPoint::new(10, 20))).to_s, "(-10,-17.5;20,35)") + + m = RBA::IMatrix3d::new(1.0, 0.5, 1.0, -0.5, 2.0, 0.0, 0.0, 0.0, 1.0) + assert_equal((m * RBA::Point::new(10, 20)).to_s, "21,35") + assert_equal((m * RBA::Vector::new(10, 20)).to_s, "20,35") + assert_equal((m * RBA::Box::new(-5, -10, 10, 20)).to_s, "(-9,-25;21,43)") + assert_equal((m * RBA::Polygon::new(RBA::Box::new(-5, -10, 10, 20))).to_s, "(6,-25;-9,-18;6,43;21,35)") + assert_equal((m * RBA::SimplePolygon::new(RBA::Box::new(-5, -10, 10, 20))).to_s, "(6,-25;-9,-18;6,43;21,35)") + assert_equal((m * RBA::Edge::new(RBA::Point::new(-5, -10), RBA::Point::new(10, 20))).to_s, "(-9,-18;21,35)") + assert_equal(RBA::Region::new(RBA::Box::new(-5, -10, 10, 20)).transformed(m).to_s, "(6,-25;-9,-18;6,43;21,35)") + r = RBA::Region::new(RBA::Box::new(-5, -10, 10, 20)) + r.transform(m) + assert_equal(r.to_s, "(6,-25;-9,-18;6,43;21,35)") + assert_equal(RBA::Edges::new(RBA::Edge::new(RBA::Point::new(-5, -10), RBA::Point::new(10, 20))).transformed(m).to_s, "(-9,-18;21,35)") + r = RBA::Edges::new(RBA::Edge::new(RBA::Point::new(-5, -10), RBA::Point::new(10, 20))) + r.transform(m) + assert_equal(r.to_s, "(-9,-18;21,35)") + assert_equal(RBA::EdgePairs::new(RBA::EdgePair::new(RBA::Edge::new(RBA::Point::new(-5, -10), RBA::Point::new(10, 20)), RBA::Edge::new(RBA::Point::new(-6, -10), RBA::Point::new(11, 20)))).transformed(m).to_s, "(-9,-18;21,35)/(-10,-17;22,35)") + r = RBA::EdgePairs::new(RBA::EdgePair::new(RBA::Edge::new(RBA::Point::new(-5, -10), RBA::Point::new(10, 20)), RBA::Edge::new(RBA::Point::new(-6, -10), RBA::Point::new(11, 20)))) + r.transform(m) + assert_equal(r.to_s, "(-9,-18;21,35)/(-10,-17;22,35)") + + m = RBA::Matrix3d::new(1.0, 0.5, 1.0, -0.5, 2.0, 0.0, 0.0, 0.0, 1.0) + assert_equal((m * RBA::DPoint::new(10, 20)).to_s, "21,35") + assert_equal((m * RBA::DVector::new(10, 20)).to_s, "20,35") + assert_equal((m * RBA::DBox::new(-5, -10, 10, 20)).to_s, "(-9,-25;21,42.5)") + assert_equal((m * RBA::DPolygon::new(RBA::DBox::new(-5, -10, 10, 20))).to_s, "(6,-25;-9,-17.5;6,42.5;21,35)") + assert_equal((m * RBA::DSimplePolygon::new(RBA::DBox::new(-5, -10, 10, 20))).to_s, "(6,-25;-9,-17.5;6,42.5;21,35)") + assert_equal((m * RBA::DEdge::new(RBA::DPoint::new(-5, -10), RBA::DPoint::new(10, 20))).to_s, "(-9,-17.5;21,35)") + + end + end load("test_epilogue.rb") diff --git a/testdata/ruby/dbRecursiveShapeIterator.rb b/testdata/ruby/dbRecursiveShapeIterator.rb index cbec7ee84..58f33c023 100644 --- a/testdata/ruby/dbRecursiveShapeIterator.rb +++ b/testdata/ruby/dbRecursiveShapeIterator.rb @@ -63,6 +63,25 @@ class DBRecursiveShapeIterator_TestClass < TestBase end + def acollect(s, l) + + res = [] + while !s.at_end? + r = "[#{l.cell_name(s.cell_index)}]" + if s.shape.is_box? + box = s.shape.box + r += box.transformed(s.always_apply_trans).to_s + else + r += "X"; + end + s.next + res.push(r) + end + + return res.join("/") + + end + def test_1 # Recursive shape iterator tests @@ -197,6 +216,19 @@ class DBRecursiveShapeIterator_TestClass < TestBase ii = RBA::RecursiveShapeIterator::new(l, c2, [0, 1], RBA::Box.new(-100, 0, 2000, 101), false) assert_equal(collect(ii, l), "[c2](0,100;1000,1200)/[c3](1100,100;2100,1200)/[c3](1101,101;2101,1201)") + ii.reset + assert_equal(acollect(ii, l), "[c2](0,100;1000,1200)/[c3](0,100;1000,1200)/[c3](1,101;1001,1201)") + + ii = RBA::RecursiveShapeIterator::new(l, c2, [0, 1], RBA::Box.new(-100, 20, 2000, 121), true) + ii.global_trans = RBA::ICplxTrans::new(RBA::Vector::new(10, 20)) + assert_equal(ii.global_trans.to_s, "r0 *1 10,20") + assert_equal(collect(ii, l), "[c2](10,120;1010,1220)/[c3](1110,120;2110,1220)") + ii.global_dtrans = RBA::DCplxTrans::new(RBA::DVector::new(0.01, 0.02)) + ii.reset + assert_equal(ii.global_dtrans.to_s, "r0 *1 0.01,0.02") + assert_equal(collect(ii, l), "[c2](10,120;1010,1220)/[c3](1110,120;2110,1220)") + ii.reset + assert_equal(acollect(ii, l), "[c2](10,120;1010,1220)/[c3](0,100;1000,1200)") ii = RBA::RecursiveShapeIterator::new(l, c2, [0, 1], RBA::Box.new(-100, 0, 2000, 101), true) assert_equal(collect(ii, l), "[c2](0,100;1000,1200)/[c3](1100,100;2100,1200)")