diff --git a/src/db/db/dbCell.h b/src/db/db/dbCell.h index e4b2f3a4a..56e5e6b9a 100644 --- a/src/db/db/dbCell.h +++ b/src/db/db/dbCell.h @@ -301,6 +301,30 @@ public: return m_instances.transform_into (ref, t); } + /** + * @brief Transforms the cell by the given transformation. + * + * The transformation is applied to all instances and shapes. Magnified transformations will + * render magnified instances. See \transform_into for a version which avoids this. + * + * @param t The transformation to apply + */ + template + void transform (const Trans &t) + { + m_instances.transform (t); + for (typename shapes_map::iterator s = m_shapes_map.begin (); s != m_shapes_map.end (); ++s) { + if (! s->second.empty ()) { + // Note: don't use the copy ctor here - it will copy the attachment to the manager + // and create problems when destroyed. Plus: swap would be more efficient. But by using + // assign_transformed we get undo support for free. + shapes_type d; + d = s->second; + s->second.assign_transformed (d, t); + } + } + } + /** * @brief Transforms the cell into a new coordinate system. * diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index a0ff3b0f6..e41ef5972 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -1563,6 +1563,28 @@ static db::Instance cell_inst_dtransform_into_cplx (db::Cell *cell, const db::In return cell->transform_into (inst, dbu_trans.inverted () * t * dbu_trans); } +static void cell_dtransform_simple (db::Cell *cell, const db::DTrans &t) +{ + const db::Layout *layout = cell->layout (); + if (! layout) { + throw tl::Exception (tl::to_string (tr ("Cell does not reside inside a layout - cannot use a micrometer-unit transformation"))); + } + + db::CplxTrans dbu_trans (layout->dbu ()); + cell->transform (db::Trans (dbu_trans.inverted () * db::DCplxTrans (t) * dbu_trans)); +} + +static void cell_dtransform_cplx (db::Cell *cell, const db::DCplxTrans &t) +{ + const db::Layout *layout = cell->layout (); + if (! layout) { + throw tl::Exception (tl::to_string (tr ("Cell does not reside inside a layout - cannot use a micrometer-unit transformation"))); + } + + db::CplxTrans dbu_trans (layout->dbu ()); + cell->transform (dbu_trans.inverted () * t * dbu_trans); +} + static void cell_dtransform_into_simple (db::Cell *cell, const db::DTrans &t) { const db::Layout *layout = cell->layout (); @@ -2347,6 +2369,46 @@ Class decl_Cell ("db", "Cell", "\n" "This variant has been introduced in version 0.25." ) + + gsi::method ("transform", (void (db::Cell::*)(const db::Trans &)) &db::Cell::transform, gsi::arg ("trans"), + "@brief Transforms the cell by the given integer transformation\n" + "\n" + "This method transforms all instances and all shapes by the given transformation. " + "There is a variant called \\transform_into which applies the transformation to instances " + "in a way such that it can be applied recursively to the child cells.\n" + "\n" + "This method has been introduced in version 0.26.7." + ) + + gsi::method ("transform", (void (db::Cell::*)(const db::ICplxTrans &)) &db::Cell::transform, gsi::arg ("trans"), + "@brief Transforms the cell by the given complex integer transformation\n" + "\n" + "This method transforms all instances and all shapes by the given transformation. " + "There is a variant called \\transform_into which applies the transformation to instances " + "in a way such that it can be applied recursively to the child cells. The difference is important in " + "the presence of magnifications: \"transform\" will leave magnified instances while \"transform_into\" " + "will not do so but expect the magnification to be applied inside the called cells too.\n" + "\n" + "This method has been introduced in version 0.26.7." + ) + + gsi::method_ext ("transform", &cell_dtransform_simple, gsi::arg ("trans"), + "@brief Transforms the cell by the given, micrometer-unit transformation\n" + "\n" + "This method transforms all instances and all shapes by the given transformation. " + "There is a variant called \\transform_into which applies the transformation to instances " + "in a way such that it can be applied recursively to the child cells.\n" + "\n" + "This method has been introduced in version 0.26.7." + ) + + gsi::method_ext ("transform", &cell_dtransform_cplx, gsi::arg ("trans"), + "@brief Transforms the cell by the given, micrometer-unit transformation\n" + "\n" + "This method transforms all instances and all shapes by the given transformation. " + "There is a variant called \\transform_into which applies the transformation to instances " + "in a way such that it can be applied recursively to the child cells. The difference is important in " + "the presence of magnifications: \"transform\" will leave magnified instances while \"transform_into\" " + "will not do so but expect the magnification to be applied inside the called cells too.\n" + "\n" + "This method has been introduced in version 0.26.7." + ) + gsi::method_ext ("transform_into", &cell_dtransform_into_simple, gsi::arg ("trans"), "@brief Transforms the cell into a new coordinate system with the given transformation where the transformation is in micrometer units\n" "This method is identical to the corresponding \\transform_into method with a \\Trans argument. For this variant " diff --git a/src/db/unit_tests/dbCellTests.cc b/src/db/unit_tests/dbCellTests.cc index 7e3dfec0e..86633b7c7 100644 --- a/src/db/unit_tests/dbCellTests.cc +++ b/src/db/unit_tests/dbCellTests.cc @@ -722,6 +722,14 @@ TEST(3a) c0.transform_into (db::ICplxTrans (ti)); inst = *c0.begin (); EXPECT_EQ (inst.to_string (), "cell_index=1 m90 -334,0"); + + c0.transform (db::Trans (5)); + inst = *c0.begin (); + EXPECT_EQ (inst.to_string (), "cell_index=1 r270 0,-334"); + + c0.transform (db::ICplxTrans (ti)); + inst = *c0.begin (); + EXPECT_EQ (inst.to_string (), "cell_index=1 r315 *2.5 600,-570"); } TEST(3b) @@ -791,6 +799,73 @@ TEST(3b) } } +TEST(3c) +{ + ::pi = 0; + + db::Manager m (true); + db::Layout g (&m); + db::Cell &c0 (g.cell (g.add_cell ())); + db::Cell &c1 (g.cell (g.add_cell ())); + + db::Trans t (db::Vector (100, -100)); + c0.insert (db::CellInstArrayWithProperties (db::CellInstArray (db::CellInst (c1.cell_index ()), t), 5)); + + db::Box b (0, 100, 1000, 1200); + c0.shapes (0).insert (db::BoxWithProperties (b, 17)); + c1.shapes (1).insert (b); + + // Note: this requires editable mode since db::Shapes::erase is permitted in editable mode only + // (erase is triggered by undo) + if (db::default_editable_mode ()) { + + m.transaction ("t"); + c0.transform (db::ICplxTrans (2.5)); + m.commit (); + + EXPECT_EQ (c1.cell_instances (), size_t (0)); + EXPECT_EQ (c0.cell_instances (), size_t (1)); + EXPECT_EQ (c0.begin ()->to_string (), "cell_index=1 r0 *2.5 250,-250 prop_id=5"); + + EXPECT_EQ (c0.shapes (0).size (), size_t (1)); + EXPECT_EQ (c0.shapes (1).size (), size_t (0)); + EXPECT_EQ (c1.shapes (0).size (), size_t (0)); + EXPECT_EQ (c1.shapes (1).size (), size_t (1)); + + EXPECT_EQ (c0.shapes (0).begin (db::ShapeIterator::All)->to_string (), "box (0,250;2500,3000) prop_id=17"); + EXPECT_EQ (c1.shapes (1).begin (db::ShapeIterator::All)->to_string (), "box (0,100;1000,1200)"); + + m.undo (); + + EXPECT_EQ (c1.cell_instances (), size_t (0)); + EXPECT_EQ (c0.cell_instances (), size_t (1)); + EXPECT_EQ (c0.begin ()->to_string (), "cell_index=1 r0 100,-100 prop_id=5"); + + EXPECT_EQ (c0.shapes (0).size (), size_t (1)); + EXPECT_EQ (c0.shapes (1).size (), size_t (0)); + EXPECT_EQ (c1.shapes (0).size (), size_t (0)); + EXPECT_EQ (c1.shapes (1).size (), size_t (1)); + + EXPECT_EQ (c0.shapes (0).begin (db::ShapeIterator::All)->to_string (), "box (0,100;1000,1200) prop_id=17"); + EXPECT_EQ (c1.shapes (1).begin (db::ShapeIterator::All)->to_string (), "box (0,100;1000,1200)"); + + m.redo (); + + EXPECT_EQ (c1.cell_instances (), size_t (0)); + EXPECT_EQ (c0.cell_instances (), size_t (1)); + EXPECT_EQ (c0.begin ()->to_string (), "cell_index=1 r0 *2.5 250,-250 prop_id=5"); + + EXPECT_EQ (c0.shapes (0).size (), size_t (1)); + EXPECT_EQ (c0.shapes (1).size (), size_t (0)); + EXPECT_EQ (c1.shapes (0).size (), size_t (0)); + EXPECT_EQ (c1.shapes (1).size (), size_t (1)); + + EXPECT_EQ (c0.shapes (0).begin (db::ShapeIterator::All)->to_string (), "box (0,250;2500,3000) prop_id=17"); + EXPECT_EQ (c1.shapes (1).begin (db::ShapeIterator::All)->to_string (), "box (0,100;1000,1200)"); + + } +} + struct map1 { db::cell_index_type operator() (db::cell_index_type i) const { return 3-i; } diff --git a/testdata/ruby/dbLayoutTest.rb b/testdata/ruby/dbLayoutTest.rb index d8b78503b..6b47d6cd2 100644 --- a/testdata/ruby/dbLayoutTest.rb +++ b/testdata/ruby/dbLayoutTest.rb @@ -1122,6 +1122,59 @@ class DBLayout_TestClass < TestBase end + # Cell#transform and Cell#transform_into + def test_14 + + g = RBA::Layout::new + c0 = g.create_cell("c0") + c1 = g.create_cell("c1") + + t = RBA::Trans::new(RBA::Vector::new(100, -100)) + inst = c0.insert(RBA::CellInstArray::new(c1.cell_index, t)) + + ti = RBA::ICplxTrans::new(2.5, 45.0, false, RBA::Vector::new(10, 20)) + t = RBA::Trans::new(1) + + assert_equal(inst.to_s, "cell_index=1 r0 100,-100") + + c0.transform_into(t) + assert_equal(inst.to_s, "cell_index=1 r0 100,100") + + c0.transform_into(ti) + assert_equal(inst.to_s, "cell_index=1 r0 0,354") + + c0.transform(t) + assert_equal(inst.to_s, "cell_index=1 r90 -354,0") + + c0.transform(ti) + assert_equal(inst.to_s, "cell_index=1 r135 *2.5 -616,-606") + + g = RBA::Layout::new + c0 = g.create_cell("c0") + c1 = g.create_cell("c1") + + t = RBA::Trans::new(RBA::Vector::new(100, -100)) + inst = c0.insert(RBA::CellInstArray::new(c1.cell_index, t)) + + ti = RBA::DCplxTrans::new(2.5, 45.0, false, RBA::DVector::new(0.01, 0.02)) + t = RBA::DTrans::new(1) + + assert_equal(inst.to_s, "cell_index=1 r0 100,-100") + + c0.transform_into(t) + assert_equal(inst.to_s, "cell_index=1 r0 100,100") + + c0.transform_into(ti) + assert_equal(inst.to_s, "cell_index=1 r0 0,354") + + c0.transform(t) + assert_equal(inst.to_s, "cell_index=1 r90 -354,0") + + c0.transform(ti) + assert_equal(inst.to_s, "cell_index=1 r135 *2.5 -616,-606") + + end + end load("test_epilogue.rb")