From d2c2a875ddc88c8613f4d4580d4ea5b43e62e46c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 13 Feb 2022 23:39:09 +0100 Subject: [PATCH 01/24] Removed debug output --- src/db/db/dbHierProcessor.cc | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/db/db/dbHierProcessor.cc b/src/db/db/dbHierProcessor.cc index 3c9d1215e..a54b9676f 100644 --- a/src/db/db/dbHierProcessor.cc +++ b/src/db/db/dbHierProcessor.cc @@ -1063,11 +1063,6 @@ private: void collect_instance_interactions (const db::CellInstArray *inst1, const db::CellInstArray *inst2) { -#if 0 -printf("@@@ check instance interactions %s (#%d, %s) <-> %s (#%d, %s)\n", - mp_subject_layout->cell_name (inst1->object ().cell_index ()), int (inst1->size ()), (inst1->bbox(db::box_convert (*mp_subject_layout)).to_string()).c_str(), - mp_intruder_layout->cell_name (inst2->object ().cell_index ()), int (inst2->size ()), (inst2->bbox(db::box_convert (*mp_intruder_layout)).to_string()).c_str()); // @@@ -#endif // TODO: this algorithm is not in particular effective for identical arrays const db::Cell &cell1 = mp_subject_layout->cell (inst1->object ().cell_index ()); @@ -1076,9 +1071,6 @@ printf("@@@ check instance interactions %s (#%d, %s) <-> %s (#%d, %s)\n", std::unordered_map > > interactions_cache; -#if 0 -printf("@@@ -> #%d\n", int(inst1->size())); -#endif for (db::CellInstArray::iterator n = inst1->begin (); ! n.at_end (); ++n) { db::ICplxTrans tn1 = inst1->complex_trans (*n); @@ -1482,9 +1474,6 @@ void local_processor::compute_contexts (local_processor_contexts::context_key_type &intruders, db::Coord dist) const { -#if 0 // @@@ -printf("@@@ --- compute_contexts (%s @ %s)\n", mp_subject_layout->cell_name (subject_cell->cell_index ()), subject_cell_inst.to_string().c_str()); fflush(stdout); -#endif CRONOLOGY_COLLECTION_BRACKET(event_compute_contexts) if (tl::verbosity () >= m_base_verbosity + 20) { From e9d86822df8333445b24a95c6556d680516bc775 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 18 Feb 2022 00:03:14 +0100 Subject: [PATCH 02/24] Scale and snap improvements * edge and edge pair support * property support * no expanding of on-grid arrays * more inline processing, better performance --- src/db/db/dbArray.h | 6 ++ src/db/db/dbLayoutUtils.cc | 199 ++++++++++++++++++++++++++++++++----- src/db/db/dbTrans.h | 46 ++++++++- 3 files changed, 224 insertions(+), 27 deletions(-) diff --git a/src/db/db/dbArray.h b/src/db/db/dbArray.h index 531f11010..44f9b2b67 100644 --- a/src/db/db/dbArray.h +++ b/src/db/db/dbArray.h @@ -2676,6 +2676,12 @@ private: template void transform_delegate (const T &tr, db::ArrayRepository *array_rep) { + // No need to do anything if there are no vector-transforming components as displacement + // is entirely handled outside the delegate + if (tr.is_unity_for_vector ()) { + return; + } + // transform the delegate if (! array_rep && ! mp_base->in_repository) { diff --git a/src/db/db/dbLayoutUtils.cc b/src/db/db/dbLayoutUtils.cc index 3f18ff33a..23a5d33a2 100644 --- a/src/db/db/dbLayoutUtils.cc +++ b/src/db/db/dbLayoutUtils.cc @@ -25,6 +25,7 @@ #include "dbCellVariants.h" #include "dbPolygonTools.h" #include "tlProgress.h" +#include "tlTimer.h" namespace db { @@ -436,9 +437,45 @@ ContextCache::find_layout_context (db::cell_index_type from, db::cell_index_type // ------------------------------------------------------------ // Scale and snap a layout +static bool +is_on_grid (const db::Vector &v, db::Coord g, db::Coord m, db::Coord d) +{ + int64_t dg = int64_t (g) * int64_t (d); + return (int64_t (v.x ()) * m) % dg == 0 && (int64_t (v.y ()) * m) % dg == 0; +} + +static void +scale_and_snap_cell_instance (db::CellInstArray &ci, const db::ICplxTrans &tr, const db::ICplxTrans &trinv, const db::Vector &delta, db::Coord g, db::Coord m, db::Coord d) +{ + db::Trans ti (ci.front ()); + + db::Vector ti_disp = ti.disp (); + ti_disp.transform (tr); + ti_disp = scaled_and_snapped_vector (ti_disp, g, m, d, delta.x (), g, m, d, delta.y ()); + ti_disp.transform (trinv); + + ci.move (ti_disp - ti.disp ()); +} + +static db::Edge +scaled_and_snapped_edge (const db::Edge &e, db::Coord g, db::Coord m, db::Coord d, db::Coord ox, db::Coord oy) +{ + int64_t dg = int64_t (g) * int64_t (d); + + int64_t x1 = snap_to_grid (int64_t (e.p1 ().x ()) * m + int64_t (ox), dg) / int64_t (d); + int64_t y1 = snap_to_grid (int64_t (e.p1 ().y ()) * m + int64_t (oy), dg) / int64_t (d); + + int64_t x2 = snap_to_grid (int64_t (e.p2 ().x ()) * m + int64_t (ox), dg) / int64_t (d); + int64_t y2 = snap_to_grid (int64_t (e.p2 ().y ()) * m + int64_t (oy), dg) / int64_t (d); + + return db::Edge (db::Point (x1, y1), db::Point (x2, y2)); +} + void scale_and_snap (db::Layout &layout, db::Cell &cell, db::Coord g, db::Coord m, db::Coord d) { + tl::SelfTimer timer (tl::verbosity () >= 31, tl::to_string (tr ("scale_and_snap"))); + if (g < 0) { throw tl::Exception (tl::to_string (tr ("Snapping requires a positive grid value"))); } @@ -453,8 +490,11 @@ scale_and_snap (db::Layout &layout, db::Cell &cell, db::Coord g, db::Coord m, db db::cell_variants_collector vars (db::ScaleAndGridReducer (g, m, d)); - vars.collect (layout, cell); - vars.separate_variants (layout, cell); + { + tl::SelfTimer timer1 (tl::verbosity () >= 41, tl::to_string (tr ("scale_and_snap: variant formation"))); + vars.collect (layout, cell); + vars.separate_variants (layout, cell); + } std::set called_cells; cell.collect_called_cells (called_cells); @@ -463,9 +503,13 @@ scale_and_snap (db::Layout &layout, db::Cell &cell, db::Coord g, db::Coord m, db db::LayoutLocker layout_locker (&layout); layout.update (); - std::vector heap; + tl::SelfTimer timer2 (tl::verbosity () >= 41, tl::to_string (tr ("scale_and_snap: snapping and scaling"))); - unsigned int work_layer = layout.insert_layer (); + std::vector heap; + std::vector iterated_array_vectors; + std::vector new_insts_with_props; + std::vector new_insts; + std::vector instances_to_replace; for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { @@ -486,7 +530,8 @@ scale_and_snap (db::Layout &layout, db::Cell &cell, db::Coord g, db::Coord m, db for (db::Layout::layer_iterator l = layout.begin_layers (); l != layout.end_layers (); ++l) { db::Shapes &s = c->shapes ((*l).first); - db::Shapes &out = c->shapes (work_layer); + + // TODO: properties, edges, edge pairs for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::Polygons | db::ShapeIterator::Paths | db::ShapeIterator::Boxes); ! si.at_end (); ++si) { @@ -495,7 +540,8 @@ scale_and_snap (db::Layout &layout, db::Cell &cell, db::Coord g, db::Coord m, db poly.transform (tr); poly = scaled_and_snapped_polygon (poly, g, m, d, tr_disp.x (), g, m, d, tr_disp.y (), heap); poly.transform (trinv); - out.insert (poly); + + s.replace (*si, poly); } @@ -506,12 +552,35 @@ scale_and_snap (db::Layout &layout, db::Cell &cell, db::Coord g, db::Coord m, db text.transform (tr); text.trans (db::Trans (text.trans ().rot (), scaled_and_snapped_vector (text.trans ().disp (), g, m, d, tr_disp.x (), g, m, d, tr_disp.y ()))); text.transform (trinv); - out.insert (text); + + s.replace (*si, text); } - s.swap (out); - out.clear (); + for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::Edges); ! si.at_end (); ++si) { + + db::Edge edge; + si->edge (edge); + edge.transform (tr); + edge = scaled_and_snapped_edge (edge, g, m , d, tr_disp.x (), tr_disp.y ()); + edge.transform (trinv); + + s.replace (*si, edge); + + } + + for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::EdgePairs); ! si.at_end (); ++si) { + + db::EdgePair edge_pair; + si->edge_pair (edge_pair); + edge_pair.transform (tr); + edge_pair = db::EdgePair (scaled_and_snapped_edge (edge_pair.first (), g, m , d, tr_disp.x (), tr_disp.y ()), + scaled_and_snapped_edge (edge_pair.second (), g, m , d, tr_disp.x (), tr_disp.y ())); + edge_pair.transform (trinv); + + s.replace (*si, edge_pair); + + } } @@ -520,33 +589,115 @@ scale_and_snap (db::Layout &layout, db::Cell &cell, db::Coord g, db::Coord m, db // as a variant cell (an effect of ScaleAndGridReducer::want_variants(cell) == true where cells have children). // Variant cells are not copied blindly back to the original layout. - std::list new_insts; + new_insts.clear (); + new_insts_with_props.clear (); + instances_to_replace.clear (); for (db::Cell::const_iterator inst = c->begin (); ! inst.at_end (); ++inst) { const db::CellInstArray &ia = inst->cell_inst (); - for (db::CellInstArray::iterator i = ia.begin (); ! i.at_end (); ++i) { - db::Trans ti (*i); - db::Vector ti_disp = ti.disp (); - ti_disp.transform (tr); - ti_disp = scaled_and_snapped_vector (ti_disp, g, m, d, tr_disp.x (), g, m, d, tr_disp.y ()); - ti_disp.transform (trinv); - ti.disp (ti_disp); + // shortcut if we do not need to explode the array + + iterated_array_vectors.clear (); + db::Vector a, b; + unsigned long na, nb; + + bool need_explode = false; + db::CellInstArray new_array (ia); + + if (tr.is_complex ()) { + + need_explode = ia.size () > 1; + + } else if (ia.is_iterated_array (&iterated_array_vectors)) { + + for (std::vector::const_iterator i = iterated_array_vectors.begin (); ! need_explode && i != iterated_array_vectors.end (); ++i) { + need_explode = ! is_on_grid (*i, g, m, d); + } + + if (! need_explode) { + + bool needs_update = false; + for (std::vector::iterator i = iterated_array_vectors.begin (); ! need_explode && i != iterated_array_vectors.end (); ++i) { + db::Vector v = scaled_and_snapped_vector (*i, g, m, d, tr_disp.x (), g, m, d, tr_disp.y ()); + if (v != *i) { + needs_update = true; + *i = v; + } + } + + if (needs_update) { + if (ia.is_complex ()) { + new_array = db::CellInstArray (ia.object (), ia.complex_trans (ia.front ()), iterated_array_vectors.begin (), iterated_array_vectors.end ()); + } else { + new_array = db::CellInstArray (ia.object (), ia.front (), iterated_array_vectors.begin (), iterated_array_vectors.end ()); + } + } + + } + + } else if (ia.is_regular_array (a, b, na, nb)) { + + need_explode = (na > 1 && ! is_on_grid (a, g, m, d)) && (nb > 1 && ! is_on_grid (b, g, m, d)); + + if (! need_explode) { + + a = scaled_and_snapped_vector (a, g, m, d, tr_disp.x (), g, m, d, tr_disp.y ()); + b = scaled_and_snapped_vector (b, g, m, d, tr_disp.x (), g, m, d, tr_disp.y ()); + + if (ia.is_complex ()) { + new_array = db::CellInstArray (ia.object (), ia.complex_trans (ia.front ()), a, b, na, nb); + } else { + new_array = db::CellInstArray (ia.object (), ia.front (), a, b, na, nb); + } + + } + + } + + if (! need_explode) { + + scale_and_snap_cell_instance (new_array, tr, trinv, tr_disp, g, m, d); + c->replace (*inst, new_array); + + } else { + + instances_to_replace.push_back (*inst); + + for (db::CellInstArray::iterator i = ia.begin (); ! i.at_end (); ++i) { + + db::Trans ti (*i); + db::Vector ti_disp = ti.disp (); + ti_disp.transform (tr); + ti_disp = scaled_and_snapped_vector (ti_disp, g, m, d, tr_disp.x (), g, m, d, tr_disp.y ()); + ti_disp.transform (trinv); + ti.disp (ti_disp); + + db::CellInstArray new_array; + if (ia.is_complex ()) { + new_array = db::CellInstArray (ia.object (), ia.complex_trans (ti)); + } else { + new_array = db::CellInstArray (ia.object (), ti); + } + + if (inst->prop_id () > 0) { + new_insts_with_props.push_back (db::CellInstArrayWithProperties (new_array, inst->prop_id ())); + } else { + new_insts.push_back (new_array); + } - if (ia.is_complex ()) { - new_insts.push_back (db::CellInstArray (ia.object (), ia.complex_trans (ti))); - } else { - new_insts.push_back (db::CellInstArray (ia.object (), ti)); } } } - c->clear_insts (); - - for (std::list::const_iterator i = new_insts.begin (); i != new_insts.end (); ++i) { + c->erase_insts (instances_to_replace); + for (std::vector::const_iterator i = new_insts.begin (); i != new_insts.end (); ++i) { + c->insert (*i); + } + for (std::vector::const_iterator i = new_insts_with_props.begin (); i != new_insts_with_props.end (); ++i) { c->insert (*i); } diff --git a/src/db/db/dbTrans.h b/src/db/db/dbTrans.h index 422f87329..5603b3873 100644 --- a/src/db/db/dbTrans.h +++ b/src/db/db/dbTrans.h @@ -77,6 +77,14 @@ struct default_trans return true; } + /** + * @brief Test, whether this is a unit transformation for vectors - i.e. ignoring displacements + */ + bool is_unity_for_vector () const + { + return true; + } + /** * @brief Orthogonal predicate * @@ -410,6 +418,14 @@ public: return m_f == 0; } + /** + * @brief Test, whether this is a unit transformation for vectors - i.e. ignoring displacements + */ + bool is_unity_for_vector () const + { + return m_f == 0; + } + /** * @brief The standard constructor using angle and mirror flag * @@ -869,6 +885,14 @@ public: return m_u.equal (displacement_type ()); } + /** + * @brief Test, whether this is a unit transformation for vectors - i.e. ignoring displacements + */ + bool is_unity_for_vector () const + { + return true; + } + /** * @brief Inversion * @@ -1311,6 +1335,14 @@ public: return m_u.equal (displacement_type ()) && fixpoint_trans::is_unity (); } + /** + * @brief Test, whether this is a unit transformation for vectors - i.e. ignoring displacements + */ + bool is_unity_for_vector () const + { + return fixpoint_trans::is_unity (); + } + /** * @brief The transformation of a distance * @@ -1874,9 +1906,9 @@ public: } /** - * @brief Test, whether this is a unit transformation + * @brief Test, whether this is a unit transformation for vectors - i.e. ignoring displacements */ - bool is_unity () const + bool is_unity_for_vector () const { if (fabs (m_mag - 1.0) > eps_f ()) { return false; @@ -1887,7 +1919,15 @@ public: if (fabs (m_cos - 1.0) > eps_f ()) { return false; } - return disp ().equal (displacement_type ()); + return true; + } + + /** + * @brief Test, whether this is a unit transformation + */ + bool is_unity () const + { + return is_unity_for_vector () && disp ().equal (displacement_type ()); } /** From 834dfa661411ebc404d61b202e423438e5c32645 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 18 Feb 2022 14:22:16 +0100 Subject: [PATCH 03/24] Bug fixes, enhancements, tests for snap and scale improvements. --- src/db/db/dbCellVariants.cc | 39 +++++++- src/db/db/dbLayoutDiff.cc | 105 ++++++++++++++++++++ src/db/db/dbLayoutDiff.h | 3 + src/db/db/dbLayoutUtils.cc | 125 +++++------------------ src/db/db/dbTestSupport.cc | 2 +- src/db/db/dbTestSupport.h | 3 +- src/db/unit_tests/dbLayoutUtilsTests.cc | 126 ++++++++++++++++++++++++ testdata/algo/layout_utils_au_sns4.oas | Bin 0 -> 1174 bytes testdata/algo/scale_and_snap4.oas | Bin 0 -> 475 bytes 9 files changed, 300 insertions(+), 103 deletions(-) create mode 100644 testdata/algo/layout_utils_au_sns4.oas create mode 100644 testdata/algo/scale_and_snap4.oas diff --git a/src/db/db/dbCellVariants.cc b/src/db/db/dbCellVariants.cc index 0126a75c3..5f36cc7b7 100644 --- a/src/db/db/dbCellVariants.cc +++ b/src/db/db/dbCellVariants.cc @@ -511,19 +511,52 @@ VariantsCollectorBase::create_var_instances_non_tl_invariant (db::Cell &in_cell, std::map >::const_iterator f = var_table.find (i->object ().cell_index ()); if (f == var_table.end ()) { - in_cell.insert (*i); + in_cell.insert (*i); } else { const std::map &vt = f->second; - for (db::CellInstArray::iterator ia = i->begin (); ! ia.at_end (); ++ia) { + bool need_explode = false; + bool first = true; + db::cell_index_type ci = 0; + + for (db::CellInstArray::iterator ia = i->begin (); ! ia.at_end () && ! need_explode; ++ia) { db::ICplxTrans rt = mp_red->reduce (for_var * mp_red->reduce_trans (i->complex_trans (*ia))); std::map::const_iterator v = vt.find (rt); tl_assert (v != vt.end ()); - in_cell.insert (db::CellInstArrayWithProperties (db::CellInstArray (db::CellInst (v->second), i->complex_trans (*ia)), i->properties_id ())); + if (first) { + ci = v->second; + first = false; + } else { + need_explode = (ci != v->second); + } + + } + + if (need_explode) { + + for (db::CellInstArray::iterator ia = i->begin (); ! ia.at_end (); ++ia) { + + db::ICplxTrans rt = mp_red->reduce (for_var * mp_red->reduce_trans (i->complex_trans (*ia))); + std::map::const_iterator v = vt.find (rt); + tl_assert (v != vt.end ()); + + in_cell.insert (db::CellInstArrayWithProperties (db::CellInstArray (db::CellInst (v->second), i->complex_trans (*ia)), i->properties_id ())); + + } + + } else if (ci != i->object ().cell_index ()) { + + db::CellInstArray new_array = *i; + new_array.object () = db::CellInst (ci); + in_cell.insert (db::CellInstArrayWithProperties (new_array, i->properties_id ())); + + } else { + + in_cell.insert (*i); } diff --git a/src/db/db/dbLayoutDiff.cc b/src/db/db/dbLayoutDiff.cc index e57cd0240..e7940a24b 100644 --- a/src/db/db/dbLayoutDiff.cc +++ b/src/db/db/dbLayoutDiff.cc @@ -287,6 +287,32 @@ private: db::Coord m_tolerance; }; +/** + * @brief A fuzzy compare operator for edge pairs + * Compares two edge pair objects applying a tolerance between them. The tolerance is the allowed deviation + * of points in database units. + */ +struct EdgePairCompareOpWithTolerance +{ + EdgePairCompareOpWithTolerance (db::Coord tolerance) + : m_ec (tolerance) + { } + + bool operator() (const db::EdgePair &a, const db::EdgePair &b) const + { + if (m_ec (a.first (), b.first ())) { + return true; + } else if (m_ec (b.first (), a.first ())) { + return false; + } else { + return m_ec (a.second (), b.second ()); + } + } + +private: + EdgeCompareOpWithTolerance m_ec; +}; + /** * @brief A fuzzy compare operator for boxes * Compares two box objects applying a tolerance between them. The tolerance is the allowed deviation @@ -492,6 +518,12 @@ make_edge_compare_func (db::Coord tolerance) return pair_compare_func > (EdgeCompareOpWithTolerance (tolerance), std_compare_func ()); } +pair_compare_func > +make_edge_pair_compare_func (db::Coord tolerance) +{ + return pair_compare_func > (EdgePairCompareOpWithTolerance (tolerance), std_compare_func ()); +} + pair_compare_func > make_box_compare_func (db::Coord tolerance) { @@ -540,6 +572,21 @@ collect_edges (const db::Layout & /*l*/, const db::Cell *c, unsigned int layer, } } +static void +collect_edge_pairs (const db::Layout & /*l*/, const db::Cell *c, unsigned int layer, unsigned int flags, std::vector< std::pair > &shapes, PropertyMapper &pn) +{ + shapes.clear (); + + for (db::ShapeIterator s = c->shapes (layer).begin (db::ShapeIterator::EdgePairs); !s.at_end (); ++s) { + db::properties_id_type prop_id = 0; + if (! (flags & layout_diff::f_no_properties)) { + prop_id = pn (s->prop_id ()); + } + shapes.push_back (std::make_pair (db::EdgePair (), prop_id)); + s->edge_pair (shapes.back ().first); + } +} + static void collect_boxes (const db::Layout &, const db::Cell *c, unsigned int layer, unsigned int flags, std::vector< std::pair > &shapes, PropertyMapper &pn) { @@ -828,6 +875,8 @@ do_compare_layouts (const db::Layout &a, const db::Cell *top_a, const db::Layout std::vector > boxes_b; std::vector > edges_a; std::vector > edges_b; + std::vector > edge_pairs_a; + std::vector > edge_pairs_b; for (unsigned int cci = 0; cci < common_cells.size (); ++cci) { @@ -1053,6 +1102,31 @@ do_compare_layouts (const db::Layout &a, const db::Cell *top_a, const db::Layout r.end_edge_differences (); } + // compare edge pairs + + edge_pairs_a.clear(); + edge_pairs_b.clear(); + if (is_valid_a) { + collect_edge_pairs (a, cell_a, layer_a, flags, edge_pairs_a, prop_normalize_a); + } + if (is_valid_b) { + collect_edge_pairs (b, cell_b, layer_b, flags, edge_pairs_b, prop_normalize_b); + } + + reduce (edge_pairs_a, edge_pairs_b, make_edge_pair_compare_func (tolerance), tolerance > 0); + + if (!edge_pairs_a.empty () || !edge_pairs_b.empty ()) { + differs = true; + if (flags & layout_diff::f_silent) { + return false; + } + r.begin_edge_pair_differences (); + if (verbose) { + r.detailed_diff (n.properties_repository (), edge_pairs_a, edge_pairs_b); + } + r.end_edge_pair_differences (); + } + r.end_layer (); } @@ -1130,6 +1204,9 @@ public: void begin_edge_differences (); void detailed_diff (const db::PropertiesRepository &pr, const std::vector > &a, const std::vector > &b); void end_edge_differences (); + void begin_edge_pair_differences (); + void detailed_diff (const db::PropertiesRepository &pr, const std::vector > &a, const std::vector > &b); + void end_edge_pair_differences (); void begin_text_differences (); void detailed_diff (const db::PropertiesRepository &pr, const std::vector > &a, const std::vector > &b); void end_text_differences (); @@ -1525,6 +1602,34 @@ PrintingDifferenceReceiver::end_edge_differences () { } +void +PrintingDifferenceReceiver::begin_edge_pair_differences () +{ + try { + enough (tl::error) << "Edge pairs differ for layer " << m_layer.to_string () << " in cell " << m_cellname; + } catch (tl::CancelException &) { + // ignore cancel exceptions + } +} + +void +PrintingDifferenceReceiver::detailed_diff (const db::PropertiesRepository &pr, const std::vector > &a, const std::vector > &b) +{ + try { + enough (tl::info) << "Not in b but in a:"; + print_diffs (pr, a, b); + enough (tl::info) << "Not in a but in b:"; + print_diffs (pr, b, a); + } catch (tl::CancelException &) { + // ignore cancel exceptions + } +} + +void +PrintingDifferenceReceiver::end_edge_pair_differences () +{ +} + void PrintingDifferenceReceiver::begin_text_differences () { diff --git a/src/db/db/dbLayoutDiff.h b/src/db/db/dbLayoutDiff.h index ed0e01e0b..4482c5009 100644 --- a/src/db/db/dbLayoutDiff.h +++ b/src/db/db/dbLayoutDiff.h @@ -119,6 +119,9 @@ public: virtual void begin_edge_differences () { } virtual void detailed_diff (const db::PropertiesRepository & /*pr*/, const std::vector > & /*a*/, const std::vector > & /*b*/) { } virtual void end_edge_differences () { } + virtual void begin_edge_pair_differences () { } + virtual void detailed_diff (const db::PropertiesRepository & /*pr*/, const std::vector > & /*a*/, const std::vector > & /*b*/) { } + virtual void end_edge_pair_differences () { } virtual void begin_text_differences () { } virtual void detailed_diff (const db::PropertiesRepository & /*pr*/, const std::vector > & /*a*/, const std::vector > & /*b*/) { } virtual void end_text_differences () { } diff --git a/src/db/db/dbLayoutUtils.cc b/src/db/db/dbLayoutUtils.cc index 23a5d33a2..2196f9fa4 100644 --- a/src/db/db/dbLayoutUtils.cc +++ b/src/db/db/dbLayoutUtils.cc @@ -507,9 +507,6 @@ scale_and_snap (db::Layout &layout, db::Cell &cell, db::Coord g, db::Coord m, db std::vector heap; std::vector iterated_array_vectors; - std::vector new_insts_with_props; - std::vector new_insts; - std::vector instances_to_replace; for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { @@ -530,8 +527,7 @@ scale_and_snap (db::Layout &layout, db::Cell &cell, db::Coord g, db::Coord m, db for (db::Layout::layer_iterator l = layout.begin_layers (); l != layout.end_layers (); ++l) { db::Shapes &s = c->shapes ((*l).first); - - // TODO: properties, edges, edge pairs + db::Shapes new_shapes; for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::Polygons | db::ShapeIterator::Paths | db::ShapeIterator::Boxes); ! si.at_end (); ++si) { @@ -541,7 +537,11 @@ scale_and_snap (db::Layout &layout, db::Cell &cell, db::Coord g, db::Coord m, db poly = scaled_and_snapped_polygon (poly, g, m, d, tr_disp.x (), g, m, d, tr_disp.y (), heap); poly.transform (trinv); - s.replace (*si, poly); + if (si->is_box () && poly.is_box ()) { + new_shapes.insert (db::BoxWithProperties (poly.box (), si->prop_id ())); + } else { + new_shapes.insert (db::PolygonWithProperties (poly, si->prop_id ())); + } } @@ -553,7 +553,7 @@ scale_and_snap (db::Layout &layout, db::Cell &cell, db::Coord g, db::Coord m, db text.trans (db::Trans (text.trans ().rot (), scaled_and_snapped_vector (text.trans ().disp (), g, m, d, tr_disp.x (), g, m, d, tr_disp.y ()))); text.transform (trinv); - s.replace (*si, text); + new_shapes.insert (db::TextWithProperties (text, si->prop_id ())); } @@ -565,7 +565,7 @@ scale_and_snap (db::Layout &layout, db::Cell &cell, db::Coord g, db::Coord m, db edge = scaled_and_snapped_edge (edge, g, m , d, tr_disp.x (), tr_disp.y ()); edge.transform (trinv); - s.replace (*si, edge); + new_shapes.insert (db::EdgeWithProperties (edge, si->prop_id ())); } @@ -578,127 +578,56 @@ scale_and_snap (db::Layout &layout, db::Cell &cell, db::Coord g, db::Coord m, db scaled_and_snapped_edge (edge_pair.second (), g, m , d, tr_disp.x (), tr_disp.y ())); edge_pair.transform (trinv); - s.replace (*si, edge_pair); + new_shapes.insert (db::EdgePairWithProperties (edge_pair, si->prop_id ())); } + s.swap (new_shapes); + } // Snap instance placements to grid and magnify // NOTE: we can modify the instances because the ScaleAndGridReducer marked every cell with children // as a variant cell (an effect of ScaleAndGridReducer::want_variants(cell) == true where cells have children). - // Variant cells are not copied blindly back to the original layout. - - new_insts.clear (); - new_insts_with_props.clear (); - instances_to_replace.clear (); + // The variant formation also made sure the iterated and regular arrays are exploded where required. for (db::Cell::const_iterator inst = c->begin (); ! inst.at_end (); ++inst) { const db::CellInstArray &ia = inst->cell_inst (); - // shortcut if we do not need to explode the array - iterated_array_vectors.clear (); db::Vector a, b; unsigned long na, nb; - bool need_explode = false; db::CellInstArray new_array (ia); - if (tr.is_complex ()) { + if (ia.is_iterated_array (&iterated_array_vectors)) { - need_explode = ia.size () > 1; - - } else if (ia.is_iterated_array (&iterated_array_vectors)) { - - for (std::vector::const_iterator i = iterated_array_vectors.begin (); ! need_explode && i != iterated_array_vectors.end (); ++i) { - need_explode = ! is_on_grid (*i, g, m, d); + bool needs_update = false; + for (std::vector::iterator i = iterated_array_vectors.begin (); i != iterated_array_vectors.end (); ++i) { + db::Vector v = scaled_and_snapped_vector (*i, g, m, d, tr_disp.x (), g, m, d, tr_disp.y ()); + if (v != *i) { + needs_update = true; + *i = v; + } } - if (! need_explode) { - - bool needs_update = false; - for (std::vector::iterator i = iterated_array_vectors.begin (); ! need_explode && i != iterated_array_vectors.end (); ++i) { - db::Vector v = scaled_and_snapped_vector (*i, g, m, d, tr_disp.x (), g, m, d, tr_disp.y ()); - if (v != *i) { - needs_update = true; - *i = v; - } - } - - if (needs_update) { - if (ia.is_complex ()) { - new_array = db::CellInstArray (ia.object (), ia.complex_trans (ia.front ()), iterated_array_vectors.begin (), iterated_array_vectors.end ()); - } else { - new_array = db::CellInstArray (ia.object (), ia.front (), iterated_array_vectors.begin (), iterated_array_vectors.end ()); - } - } - + if (needs_update) { + new_array = db::CellInstArray (ia.object (), ia.complex_trans (ia.front ()), iterated_array_vectors.begin (), iterated_array_vectors.end ()); } } else if (ia.is_regular_array (a, b, na, nb)) { - need_explode = (na > 1 && ! is_on_grid (a, g, m, d)) && (nb > 1 && ! is_on_grid (b, g, m, d)); + a = scaled_and_snapped_vector (a, g, m, d, tr_disp.x (), g, m, d, tr_disp.y ()); + b = scaled_and_snapped_vector (b, g, m, d, tr_disp.x (), g, m, d, tr_disp.y ()); - if (! need_explode) { - - a = scaled_and_snapped_vector (a, g, m, d, tr_disp.x (), g, m, d, tr_disp.y ()); - b = scaled_and_snapped_vector (b, g, m, d, tr_disp.x (), g, m, d, tr_disp.y ()); - - if (ia.is_complex ()) { - new_array = db::CellInstArray (ia.object (), ia.complex_trans (ia.front ()), a, b, na, nb); - } else { - new_array = db::CellInstArray (ia.object (), ia.front (), a, b, na, nb); - } - - } + new_array = db::CellInstArray (ia.object (), ia.complex_trans (ia.front ()), a, b, na, nb); } - if (! need_explode) { + scale_and_snap_cell_instance (new_array, tr, trinv, tr_disp, g, m, d); + c->replace (*inst, new_array); - scale_and_snap_cell_instance (new_array, tr, trinv, tr_disp, g, m, d); - c->replace (*inst, new_array); - - } else { - - instances_to_replace.push_back (*inst); - - for (db::CellInstArray::iterator i = ia.begin (); ! i.at_end (); ++i) { - - db::Trans ti (*i); - db::Vector ti_disp = ti.disp (); - ti_disp.transform (tr); - ti_disp = scaled_and_snapped_vector (ti_disp, g, m, d, tr_disp.x (), g, m, d, tr_disp.y ()); - ti_disp.transform (trinv); - ti.disp (ti_disp); - - db::CellInstArray new_array; - if (ia.is_complex ()) { - new_array = db::CellInstArray (ia.object (), ia.complex_trans (ti)); - } else { - new_array = db::CellInstArray (ia.object (), ti); - } - - if (inst->prop_id () > 0) { - new_insts_with_props.push_back (db::CellInstArrayWithProperties (new_array, inst->prop_id ())); - } else { - new_insts.push_back (new_array); - } - - } - - } - - } - - c->erase_insts (instances_to_replace); - for (std::vector::const_iterator i = new_insts.begin (); i != new_insts.end (); ++i) { - c->insert (*i); - } - for (std::vector::const_iterator i = new_insts_with_props.begin (); i != new_insts_with_props.end (); ++i) { - c->insert (*i); } } diff --git a/src/db/db/dbTestSupport.cc b/src/db/db/dbTestSupport.cc index aa5522df0..090e849a2 100644 --- a/src/db/db/dbTestSupport.cc +++ b/src/db/db/dbTestSupport.cc @@ -138,7 +138,7 @@ void compare_layouts (tl::TestBase *_this, const db::Layout &layout, const std:: equal = db::compare_layouts (*subject, layout_au, (n > 0 ? db::layout_diff::f_silent : db::layout_diff::f_verbose) | ((norm & AsPolygons) != 0 ? db::layout_diff::f_boxes_as_polygons + db::layout_diff::f_paths_as_polygons : 0) - | db::layout_diff::f_flatten_array_insts + | ((norm & WithArrays) != 0 ? 0 : db::layout_diff::f_flatten_array_insts) /*| db::layout_diff::f_no_text_details | db::layout_diff::f_no_text_orientation*/ , tolerance, 100 /*max diff lines*/); if (equal && n > 0) { diff --git a/src/db/db/dbTestSupport.h b/src/db/db/dbTestSupport.h index 9b031fdce..8f89cf7e9 100644 --- a/src/db/db/dbTestSupport.h +++ b/src/db/db/dbTestSupport.h @@ -56,7 +56,8 @@ enum NormalizationMode WriteOAS = 2, // normalize subject by writing to OASIS and reading back NormFileMask = 7, // bits the extract for file mode NoContext = 8, // write tmp file without context - AsPolygons = 16 // paths and boxes are treated as polygons + AsPolygons = 16, // paths and boxes are treated as polygons + WithArrays = 32 // do not flatten arrays }; /** diff --git a/src/db/unit_tests/dbLayoutUtilsTests.cc b/src/db/unit_tests/dbLayoutUtilsTests.cc index 3dac06878..26130e050 100644 --- a/src/db/unit_tests/dbLayoutUtilsTests.cc +++ b/src/db/unit_tests/dbLayoutUtilsTests.cc @@ -25,6 +25,8 @@ #include "dbCellMapping.h" #include "dbTestSupport.h" #include "dbReader.h" +#include "dbLayoutDiff.h" +#include "dbPropertiesRepository.h" #include "tlString.h" #include "tlUnitTest.h" @@ -653,3 +655,127 @@ TEST(18_scale_and_snap) CHECKPOINT(); db::compare_layouts (_this, l1, tl::testdata () + "/algo/layout_utils_au_sns3.gds"); } + +TEST(19_scale_and_snap_basic) +{ + db::Layout l1; + db::Layout l2; + + db::PropertiesRepository::properties_set ps1; + ps1.insert (std::make_pair (l1.properties_repository ().prop_name_id (tl::Variant ("p")), tl::Variant (17))); + db::properties_id_type pid1 = l1.properties_repository ().properties_id (ps1); + + db::PropertiesRepository::properties_set ps2; + ps2.insert (std::make_pair (l2.properties_repository ().prop_name_id (tl::Variant ("p")), tl::Variant (17))); + db::properties_id_type pid2 = l2.properties_repository ().properties_id (ps2); + + db::Cell &top1 = l1.cell (l1.add_cell ("TOP")); + db::Cell &top2 = l2.cell (l2.add_cell ("TOP")); + + db::Cell &a1 = l1.cell (l1.add_cell ("A")); + db::Cell &a2a = l2.cell (l2.add_cell ("A")); + + unsigned int layer1 = l1.insert_layer (db::LayerProperties (1, 0)); + unsigned int layer2 = l2.insert_layer (db::LayerProperties (1, 0)); + + a1.shapes (layer1).insert (db::Box (0, 0, 100, 100)); + a2a.shapes (layer2).insert (db::Box (0, 0, 100, 100)); + + top1.shapes (layer1).insert (db::Box (11, 21, 31, 41)); + top2.shapes (layer2).insert (db::Box (10, 20, 30, 40)); + + top1.shapes (layer1).insert (db::BoxWithProperties (db::Box (11, 21, 31, 41), pid1)); + top2.shapes (layer2).insert (db::BoxWithProperties (db::Box (10, 20, 30, 40), pid2)); + + top1.shapes (layer1).insert (db::Edge (11, 21, 31, 41)); + top2.shapes (layer2).insert (db::Edge (10, 20, 30, 40)); + + top1.shapes (layer1).insert (db::EdgeWithProperties (db::Edge (11, 21, 31, 41), pid1)); + top2.shapes (layer2).insert (db::EdgeWithProperties (db::Edge (10, 20, 30, 40), pid2)); + + top1.shapes (layer1).insert (db::EdgePair (db::Edge (11, 21, 31, 41), db::Edge (111, 121, 131, 141))); + top2.shapes (layer2).insert (db::EdgePair (db::Edge (10, 20, 30, 40), db::Edge (110, 120, 130, 140))); + + top1.shapes (layer1).insert (db::EdgePairWithProperties (db::EdgePair (db::Edge (11, 21, 31, 41), db::Edge (111, 121, 131, 141)), pid1)); + top2.shapes (layer2).insert (db::EdgePairWithProperties (db::EdgePair (db::Edge (10, 20, 30, 40), db::Edge (110, 120, 130, 140)), pid2)); + + top1.shapes (layer1).insert (db::Polygon (db::Box (11, 21, 31, 41))); + top2.shapes (layer2).insert (db::Polygon (db::Box (10, 20, 30, 40))); + + top1.shapes (layer1).insert (db::PolygonWithProperties (db::Polygon (db::Box (11, 21, 31, 41)), pid1)); + top2.shapes (layer2).insert (db::PolygonWithProperties (db::Polygon (db::Box (10, 20, 30, 40)), pid2)); + + db::Point pts1[] = { + db::Point (1, 101), + db::Point (101, 101), + db::Point (101, 201) + }; + + db::Point pts2[] = { + db::Point (0, 100), + db::Point (100, 100), + db::Point (100, 200) + }; + + top1.shapes (layer1).insert (db::Path (&pts1 [0], &pts1 [sizeof (pts1) / sizeof(pts1 [0])], 20)); + top2.shapes (layer2).insert (db::Path (&pts2 [0], &pts2 [sizeof (pts2) / sizeof(pts2 [0])], 20)); + + top1.shapes (layer1).insert (db::PathWithProperties (db::Path (&pts1 [0], &pts1 [sizeof (pts1) / sizeof(pts1 [0])], 20), pid1)); + top2.shapes (layer2).insert (db::PathWithProperties (db::Path (&pts2 [0], &pts2 [sizeof (pts2) / sizeof(pts2 [0])], 20), pid2)); + + top1.shapes (layer1).insert (db::Text ("t1", db::Trans (db::Vector (11, 21)))); + top2.shapes (layer2).insert (db::Text ("t1", db::Trans (db::Vector (10, 20)))); + + top1.shapes (layer1).insert (db::TextWithProperties (db::Text ("t1", db::Trans (db::Vector (11, 21))), pid1)); + top2.shapes (layer2).insert (db::TextWithProperties (db::Text ("t1", db::Trans (db::Vector (10, 20))), pid2)); + + top1.insert (db::CellInstArray (db::CellInst (a1.cell_index ()), db::Trans (db::Vector (11, 21)))); + top2.insert (db::CellInstArray (db::CellInst (a2a.cell_index ()), db::Trans (db::Vector (10, 20)))); + + top1.insert (db::CellInstArrayWithProperties (db::CellInstArray (db::CellInst (a1.cell_index ()), db::Trans (db::Vector (11, 21))), pid1)); + top2.insert (db::CellInstArrayWithProperties (db::CellInstArray (db::CellInst (a2a.cell_index ()), db::Trans (db::Vector (10, 20))), pid2)); + + top1.insert (db::CellInstArray (db::CellInst (a1.cell_index ()), db::Trans (db::Vector (11, 21)), db::Vector (0, 10), db::Vector (10, 0), 2, 3)); + top2.insert (db::CellInstArray (db::CellInst (a2a.cell_index ()), db::Trans (db::Vector (10, 20)), db::Vector (0, 10), db::Vector (10, 0), 2, 3)); + + top1.insert (db::CellInstArrayWithProperties (db::CellInstArray (db::CellInst (a1.cell_index ()), db::Trans (db::Vector (11, 21)), db::Vector (0, 10), db::Vector (10, 0), 2, 3), pid1)); + top2.insert (db::CellInstArrayWithProperties (db::CellInstArray (db::CellInst (a2a.cell_index ()), db::Trans (db::Vector (10, 20)), db::Vector (0, 10), db::Vector (10, 0), 2, 3), pid2)); + + std::vector ia; + ia.push_back (db::Vector (0, 0)); + ia.push_back (db::Vector (10, 0)); + ia.push_back (db::Vector (0, 10)); + + top1.insert (db::CellInstArray (db::CellInst (a1.cell_index ()), db::Trans (db::Vector (11, 21)), ia.begin (), ia.end ())); + top2.insert (db::CellInstArray (db::CellInst (a2a.cell_index ()), db::Trans (db::Vector (10, 20)), ia.begin (), ia.end ())); + + top1.insert (db::CellInstArrayWithProperties (db::CellInstArray (db::CellInst (a1.cell_index ()), db::Trans (db::Vector (11, 21)), ia.begin (), ia.end ()), pid1)); + top2.insert (db::CellInstArrayWithProperties (db::CellInstArray (db::CellInst (a2a.cell_index ()), db::Trans (db::Vector (10, 20)), ia.begin (), ia.end ()), pid2)); + + db::scale_and_snap (l1, top1, 10, 1, 1); + + bool equal = db::compare_layouts (l1, l2, + db::layout_diff::f_verbose + | db::layout_diff::f_boxes_as_polygons + | db::layout_diff::f_paths_as_polygons + , 0, 100 /*max diff lines*/); + EXPECT_EQ (equal, true); +} + +TEST(20_scale_and_snap) +{ + db::Layout l1; + { + std::string fn (tl::testdata ()); + fn += "/algo/scale_and_snap4.oas"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (l1); + } + + db::scale_and_snap (l1, l1.cell (*l1.begin_top_down ()), 10, 95, 100); + + CHECKPOINT(); + db::compare_layouts (_this, l1, tl::testdata () + "/algo/layout_utils_au_sns4.oas", db::NormalizationMode (db::WriteOAS + db::WithArrays)); +} + diff --git a/testdata/algo/layout_utils_au_sns4.oas b/testdata/algo/layout_utils_au_sns4.oas new file mode 100644 index 0000000000000000000000000000000000000000..3213ccba4901ba5331540e81342bcea159a88c1a GIT binary patch literal 1174 zcmd_ozfaU)7zgmaeS6pAdWCyS%fV4N5^!*U(6{_DF)@ua*aQwkPl3gb{s+!FnA||3 ziHrGV_3qlj9~@C zDuy)-*vzq+V>8EQj?EmKIW}`_=Ge@!nPan$&Aw;O*q?}!SIiDL4NieBI0?4F32+>2 zfn%Tp+TbW?fedVdBj7M-f(_6B>);Sr19hM{qu9V7?O)zzp%5Gp`MYOs^2`(+DuroOpfPje*4QVv8+@5 zRA*xJ@IyINlS2dY?S+;R4CduxWH!_@V0gjKfDB|rrGn#q9V6m{J>C6WUE)3cLR{TlgW|(I zT|zuKSY&u*Akv|J*c8Z!as|hS_y@#0yZZR>Fauf4V1m(+mr>*}Bf|nVUIxKGjF%QM zy-?s{Y&W{aa#5p=Pq1RbQRWA+jLgjU*zd6mR&+Sbh+z~GdcppJU9jju><$B=GN>be l*fZ$w&ef- literal 0 HcmV?d00001 From a133a8f43b88634018221ab87920e0a5085d7533 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 18 Feb 2022 18:43:02 +0100 Subject: [PATCH 04/24] Reverted some changes, removed idle code --- src/db/db/dbArray.h | 6 ----- src/db/db/dbLayoutUtils.cc | 7 ------ src/db/db/dbTrans.h | 46 +++----------------------------------- 3 files changed, 3 insertions(+), 56 deletions(-) diff --git a/src/db/db/dbArray.h b/src/db/db/dbArray.h index 44f9b2b67..531f11010 100644 --- a/src/db/db/dbArray.h +++ b/src/db/db/dbArray.h @@ -2676,12 +2676,6 @@ private: template void transform_delegate (const T &tr, db::ArrayRepository *array_rep) { - // No need to do anything if there are no vector-transforming components as displacement - // is entirely handled outside the delegate - if (tr.is_unity_for_vector ()) { - return; - } - // transform the delegate if (! array_rep && ! mp_base->in_repository) { diff --git a/src/db/db/dbLayoutUtils.cc b/src/db/db/dbLayoutUtils.cc index 2196f9fa4..319a878d9 100644 --- a/src/db/db/dbLayoutUtils.cc +++ b/src/db/db/dbLayoutUtils.cc @@ -437,13 +437,6 @@ ContextCache::find_layout_context (db::cell_index_type from, db::cell_index_type // ------------------------------------------------------------ // Scale and snap a layout -static bool -is_on_grid (const db::Vector &v, db::Coord g, db::Coord m, db::Coord d) -{ - int64_t dg = int64_t (g) * int64_t (d); - return (int64_t (v.x ()) * m) % dg == 0 && (int64_t (v.y ()) * m) % dg == 0; -} - static void scale_and_snap_cell_instance (db::CellInstArray &ci, const db::ICplxTrans &tr, const db::ICplxTrans &trinv, const db::Vector &delta, db::Coord g, db::Coord m, db::Coord d) { diff --git a/src/db/db/dbTrans.h b/src/db/db/dbTrans.h index 5603b3873..422f87329 100644 --- a/src/db/db/dbTrans.h +++ b/src/db/db/dbTrans.h @@ -77,14 +77,6 @@ struct default_trans return true; } - /** - * @brief Test, whether this is a unit transformation for vectors - i.e. ignoring displacements - */ - bool is_unity_for_vector () const - { - return true; - } - /** * @brief Orthogonal predicate * @@ -418,14 +410,6 @@ public: return m_f == 0; } - /** - * @brief Test, whether this is a unit transformation for vectors - i.e. ignoring displacements - */ - bool is_unity_for_vector () const - { - return m_f == 0; - } - /** * @brief The standard constructor using angle and mirror flag * @@ -885,14 +869,6 @@ public: return m_u.equal (displacement_type ()); } - /** - * @brief Test, whether this is a unit transformation for vectors - i.e. ignoring displacements - */ - bool is_unity_for_vector () const - { - return true; - } - /** * @brief Inversion * @@ -1335,14 +1311,6 @@ public: return m_u.equal (displacement_type ()) && fixpoint_trans::is_unity (); } - /** - * @brief Test, whether this is a unit transformation for vectors - i.e. ignoring displacements - */ - bool is_unity_for_vector () const - { - return fixpoint_trans::is_unity (); - } - /** * @brief The transformation of a distance * @@ -1906,9 +1874,9 @@ public: } /** - * @brief Test, whether this is a unit transformation for vectors - i.e. ignoring displacements + * @brief Test, whether this is a unit transformation */ - bool is_unity_for_vector () const + bool is_unity () const { if (fabs (m_mag - 1.0) > eps_f ()) { return false; @@ -1919,15 +1887,7 @@ public: if (fabs (m_cos - 1.0) > eps_f ()) { return false; } - return true; - } - - /** - * @brief Test, whether this is a unit transformation - */ - bool is_unity () const - { - return is_unity_for_vector () && disp ().equal (displacement_type ()); + return disp ().equal (displacement_type ()); } /** From c5da9c717d6ce9c5197b973462f6289ba43ef6a8 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 18 Feb 2022 18:49:00 +0100 Subject: [PATCH 05/24] scale_and_snap: separate handling of shapes with properties and without --- src/db/db/dbLayoutUtils.cc | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/db/db/dbLayoutUtils.cc b/src/db/db/dbLayoutUtils.cc index 319a878d9..82ead48ce 100644 --- a/src/db/db/dbLayoutUtils.cc +++ b/src/db/db/dbLayoutUtils.cc @@ -531,9 +531,17 @@ scale_and_snap (db::Layout &layout, db::Cell &cell, db::Coord g, db::Coord m, db poly.transform (trinv); if (si->is_box () && poly.is_box ()) { - new_shapes.insert (db::BoxWithProperties (poly.box (), si->prop_id ())); + if (si->has_prop_id ()) { + new_shapes.insert (db::BoxWithProperties (poly.box (), si->prop_id ())); + } else { + new_shapes.insert (poly.box ()); + } } else { - new_shapes.insert (db::PolygonWithProperties (poly, si->prop_id ())); + if (si->has_prop_id ()) { + new_shapes.insert (db::PolygonWithProperties (poly, si->prop_id ())); + } else { + new_shapes.insert (poly); + } } } @@ -546,7 +554,11 @@ scale_and_snap (db::Layout &layout, db::Cell &cell, db::Coord g, db::Coord m, db text.trans (db::Trans (text.trans ().rot (), scaled_and_snapped_vector (text.trans ().disp (), g, m, d, tr_disp.x (), g, m, d, tr_disp.y ()))); text.transform (trinv); - new_shapes.insert (db::TextWithProperties (text, si->prop_id ())); + if (si->has_prop_id ()) { + new_shapes.insert (db::TextWithProperties (text, si->prop_id ())); + } else { + new_shapes.insert (text); + } } @@ -558,7 +570,11 @@ scale_and_snap (db::Layout &layout, db::Cell &cell, db::Coord g, db::Coord m, db edge = scaled_and_snapped_edge (edge, g, m , d, tr_disp.x (), tr_disp.y ()); edge.transform (trinv); - new_shapes.insert (db::EdgeWithProperties (edge, si->prop_id ())); + if (si->has_prop_id ()) { + new_shapes.insert (db::EdgeWithProperties (edge, si->prop_id ())); + } else { + new_shapes.insert (edge); + } } @@ -571,7 +587,11 @@ scale_and_snap (db::Layout &layout, db::Cell &cell, db::Coord g, db::Coord m, db scaled_and_snapped_edge (edge_pair.second (), g, m , d, tr_disp.x (), tr_disp.y ())); edge_pair.transform (trinv); - new_shapes.insert (db::EdgePairWithProperties (edge_pair, si->prop_id ())); + if (si->has_prop_id ()) { + new_shapes.insert (db::EdgePairWithProperties (edge_pair, si->prop_id ())); + } else { + new_shapes.insert (edge_pair); + } } From 30f774f0555013a4bc281091e9bde4135b43c0a1 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 19 Feb 2022 17:57:41 +0100 Subject: [PATCH 06/24] WIP: dynamic definition of macro categories (currently: drc, lvs) --- .../drc/built-in-macros/drc_interpreters.lym | 5 ++ src/lay/lay/gsiDeclLayApplication.cc | 12 +++++ src/lay/lay/layApplication.cc | 43 +++++++++++++++- src/lay/lay/layApplication.h | 7 +++ src/lay/lay/layMacroController.cc | 51 +++---------------- src/lay/lay/layMacroController.h | 8 ++- .../lvs/built-in-macros/lvs_interpreters.lym | 5 ++ src/lym/lym/lymMacro.cc | 46 +++++++++++++---- src/lym/lym/lymMacro.h | 12 ++++- 9 files changed, 132 insertions(+), 57 deletions(-) diff --git a/src/drc/drc/built-in-macros/drc_interpreters.lym b/src/drc/drc/built-in-macros/drc_interpreters.lym index 4a0108d44..a5c4d13ed 100644 --- a/src/drc/drc/built-in-macros/drc_interpreters.lym +++ b/src/drc/drc/built-in-macros/drc_interpreters.lym @@ -158,6 +158,11 @@ module DRC # Register the new interpreters DRCInterpreter::new(drc_recipe) DRCPlainTextInterpreter::new(drc_recipe) + + # Creates a new macro category + if RBA::Application::instance + RBA::Application::instance.add_macro_category("drc", "DRC", [ "drc" ]) + end end diff --git a/src/lay/lay/gsiDeclLayApplication.cc b/src/lay/lay/gsiDeclLayApplication.cc index 287b80f92..fbf23517b 100644 --- a/src/lay/lay/gsiDeclLayApplication.cc +++ b/src/lay/lay/gsiDeclLayApplication.cc @@ -87,6 +87,12 @@ static std::string version (C *) return C::version (); } +template +static void add_macro_category (C *c, const std::string &name, const std::string &description, const std::vector &folders) +{ + c->add_macro_category (name, description, folders); +} + template static gsi::Methods application_methods () { @@ -226,6 +232,12 @@ static gsi::Methods application_methods () "@brief Returns the architecture string\n" "This method has been introduced in version 0.25." ) + + method_ext &> ("add_macro_category", &add_macro_category, gsi::arg ("name"), gsi::arg ("description"), gsi::arg ("folders"), + "@brief Creates a new macro category\n" + "Creating a new macro category is only possible during the autorun_early stage. " + "The new macro category must correspond to an interpreter registered at the same stage.\n" + "This method has been introduced in version 0.28." + ) + method ("instance", &C::instance, "@brief Return the singleton instance of the application\n" "\n" diff --git a/src/lay/lay/layApplication.cc b/src/lay/lay/layApplication.cc index f5d4dd505..a66c633a2 100644 --- a/src/lay/lay/layApplication.cc +++ b/src/lay/lay/layApplication.cc @@ -686,6 +686,22 @@ ApplicationBase::init_app () if (mc) { + // create the basic macro categories + + if (ruby_interpreter ().available ()) { + std::vector folders; + folders.push_back ("macros"); + folders.push_back ("ruby"); + mc->add_macro_category ("macros", "Ruby", folders); + } + + if (python_interpreter ().available ()) { + std::vector folders; + folders.push_back ("pymacros"); + folders.push_back ("python"); + mc->add_macro_category ("pymacros", "Python", folders); + } + mc->enable_implicit_macros (! m_no_macros); // Add the global ruby modules as the first ones. @@ -741,13 +757,29 @@ ApplicationBase::init_app () } } + std::set already_executed; + // run all early autorun macros - lym::MacroCollection::root ().autorun_early (); + lym::MacroCollection::root ().autorun_early (&already_executed); + + // autorun_early may have added macro categories, so we need to call finish() again + if (mc) { + + mc->finish (); + + // as this regenerates the macro collection, autorun_early is required again + // note: this does no re-execute macros that have been executed already + lym::MacroCollection::root ().autorun_early (&already_executed); + + } // rescan the folders because early autorun macros might have added // suffixes through the MacroInterpreter interface. lym::MacroCollection::root ().rescan (); + // and yet another autorun_early pass .. + lym::MacroCollection::root ().autorun_early (&already_executed); + // creates the main window or plugin root as required setup (); @@ -785,6 +817,15 @@ ApplicationBase::init_app () } } +void +ApplicationBase::add_macro_category (const std::string &name, const std::string &description, const std::vector &folders) +{ + lay::MacroController *mc = lay::MacroController::instance (); + if (mc) { + mc->add_macro_category (name, description, folders); + } +} + ApplicationBase::~ApplicationBase () { tl::set_ui_exception_handlers (0, 0, 0); diff --git a/src/lay/lay/layApplication.h b/src/lay/lay/layApplication.h index 85eb65267..887fc42c5 100644 --- a/src/lay/lay/layApplication.h +++ b/src/lay/lay/layApplication.h @@ -214,6 +214,13 @@ public: return *mp_python_interpreter; } + /** + * @brief Adds a new macro category + * + * This method is only effective when called during the autorun_early stage + */ + void add_macro_category (const std::string &name, const std::string &description, const std::vector &folders); + /** * @brief Return true, if undo buffering is enabled */ diff --git a/src/lay/lay/layMacroController.cc b/src/lay/lay/layMacroController.cc index 77bd03e20..51b1111ef 100644 --- a/src/lay/lay/layMacroController.cc +++ b/src/lay/lay/layMacroController.cc @@ -49,62 +49,27 @@ MacroController::MacroController () // .. nothing yet .. } -static lay::MacroController::MacroCategory ruby_cat () +void +MacroController::add_macro_category (const std::string &name, const std::string &description, const std::vector &folders) { lay::MacroController::MacroCategory cat; - cat.name = "macros"; - cat.description = tl::to_string (QObject::tr ("Ruby")); - cat.folders.push_back ("macros"); - cat.folders.push_back ("ruby"); - return cat; -} - -static lay::MacroController::MacroCategory python_cat () -{ - lay::MacroController::MacroCategory cat; - cat.name = "pymacros"; - cat.description = tl::to_string (QObject::tr ("Python")); - cat.folders.push_back ("pymacros"); - cat.folders.push_back ("python"); - return cat; -} - -static lay::MacroController::MacroCategory drc_cat () -{ - lay::MacroController::MacroCategory cat; - cat.name = "drc"; - cat.description = tl::to_string (QObject::tr ("DRC")); - cat.folders.push_back ("drc"); - return cat; -} - -static lay::MacroController::MacroCategory lvs_cat () -{ - lay::MacroController::MacroCategory cat; - cat.name = "lvs"; - cat.description = tl::to_string (QObject::tr ("LVS")); - cat.folders.push_back ("lvs"); - return cat; + cat.name = name; + cat.description = description; + cat.folders = folders; + m_macro_categories.push_back (cat); } void MacroController::finish () { + lym::MacroCollection::root ().clear (); + // Scan built-in macros // These macros are always taken, even if there are no macros requested (they are required to // fully form the API). lym::MacroCollection::root ().add_folder (tl::to_string (QObject::tr ("Built-In")), ":/built-in-macros", "macros", true); lym::MacroCollection::root ().add_folder (tl::to_string (QObject::tr ("Built-In")), ":/built-in-pymacros", "pymacros", true); - // TODO: consider adding "drc" and "lvs" dynamically and allow more dynamic categories - // We can do so if we first load the macros with the initial interpreters, then do autorun (which creates DSL interpreters) and then - // register the remaining categories. - - m_macro_categories.push_back (ruby_cat ()); - m_macro_categories.push_back (python_cat ()); - m_macro_categories.push_back (drc_cat ()); - m_macro_categories.push_back (lvs_cat ()); - // scans the macros from techs and packages (this will allow autorun-early on them) // and updates m_external_paths sync_macro_sources (); diff --git a/src/lay/lay/layMacroController.h b/src/lay/lay/layMacroController.h index 757d92653..cae8e979b 100644 --- a/src/lay/lay/layMacroController.h +++ b/src/lay/lay/layMacroController.h @@ -147,10 +147,16 @@ public: /** * @brief Loads the macros from the predefined paths and establishes the search paths - * This method will also establish the macro categories. + * This method can be called multiple times. */ void finish (); + /** + * @brief Adds a new macro category + * finish() needs to be called after adding a new category. + */ + void add_macro_category (const std::string &name, const std::string &description, const std::vector &folders); + /** * @brief Adds a temporary macro * diff --git a/src/lvs/lvs/built-in-macros/lvs_interpreters.lym b/src/lvs/lvs/built-in-macros/lvs_interpreters.lym index f9f9863f8..24cd20021 100644 --- a/src/lvs/lvs/built-in-macros/lvs_interpreters.lym +++ b/src/lvs/lvs/built-in-macros/lvs_interpreters.lym @@ -159,6 +159,11 @@ module LVS LVSInterpreter::new(lvs_recipe) LVSPlainTextInterpreter::new(lvs_recipe) + # Creates a new macro category + if RBA::Application::instance + RBA::Application::instance.add_macro_category("lvs", "LVS", [ "lvs" ]) + end + end diff --git a/src/lym/lym/lymMacro.cc b/src/lym/lym/lymMacro.cc index 8067b67df..f7e34fe36 100644 --- a/src/lym/lym/lymMacro.cc +++ b/src/lym/lym/lymMacro.cc @@ -46,6 +46,8 @@ #include #include +#include +#include namespace lym { @@ -1065,6 +1067,11 @@ MacroCollection::MacroCollection () } MacroCollection::~MacroCollection () +{ + do_clear (); +} + +void MacroCollection::do_clear () { for (iterator m = begin (); m != end (); ++m) { delete m->second; @@ -1513,6 +1520,13 @@ void MacroCollection::scan (const std::string &path) } } +void MacroCollection::clear () +{ + begin_changes (); + do_clear (); + on_changed (); +} + void MacroCollection::erase (lym::Macro *mp) { for (iterator m = m_macros.begin (); m != m_macros.end (); ++m) { @@ -1861,30 +1875,42 @@ bool MacroCollection::has_autorun_early () const return has_autorun_for (*this, true); } -static void autorun_for (lym::MacroCollection &collection, bool early) +static void autorun_for (lym::MacroCollection &collection, bool early, std::set *executed_already) { for (lym::MacroCollection::child_iterator c = collection.begin_children (); c != collection.end_children (); ++c) { - autorun_for (*c->second, early); + autorun_for (*c->second, early, executed_already); } for (lym::MacroCollection::iterator c = collection.begin (); c != collection.end (); ++c) { + if (c->second->can_run () && ((early && c->second->is_autorun_early ()) || (!early && c->second->is_autorun () && !c->second->is_autorun_early ()))) { - BEGIN_PROTECTED_SILENT - c->second->run (); - c->second->install_doc (); - END_PROTECTED_SILENT + + if (!executed_already || executed_already->find (c->second->path ()) == executed_already->end ()) { + + BEGIN_PROTECTED_SILENT + c->second->run (); + c->second->install_doc (); + END_PROTECTED_SILENT + + if (executed_already) { + executed_already->insert (c->second->path ()); + } + + } + } + } } -void MacroCollection::autorun () +void MacroCollection::autorun (std::set *already_executed) { - autorun_for (*this, false); + autorun_for (*this, false, already_executed); } -void MacroCollection::autorun_early () +void MacroCollection::autorun_early (std::set *already_executed) { - autorun_for (*this, true); + autorun_for (*this, true, already_executed); } void MacroCollection::dump (int l) diff --git a/src/lym/lym/lymMacro.h b/src/lym/lym/lymMacro.h index 396bdbee3..063901ca4 100644 --- a/src/lym/lym/lymMacro.h +++ b/src/lym/lym/lymMacro.h @@ -845,6 +845,12 @@ public: */ void add_unspecific (lym::Macro *m); + /** + * @brief Empties the collection + * Note: only the unspecific on_changed event is generated. + */ + void clear (); + /** * @brief Erases the given macro from the list * @@ -995,7 +1001,7 @@ public: /** * @brief Runs all macros marked with auto-run */ - void autorun (); + void autorun (std::set *already_executed = 0); /** * @brief Returns true, if the collection has an early autorun macro @@ -1005,7 +1011,7 @@ public: /** * @brief Runs all macros marked with early auto-run */ - void autorun_early (); + void autorun_early (std::set *already_executed = 0); /** * @brief Redo the scan (will add new files or folders) @@ -1129,6 +1135,8 @@ private: m_readonly = f; } + void do_clear (); + // no copying MacroCollection (const MacroCollection &d); MacroCollection &operator= (const MacroCollection &d); From fdb7d90550c7c18d8ba88219e4b5a405a8868bd0 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 24 Feb 2022 00:34:30 +0100 Subject: [PATCH 07/24] Introducing auto-run priorities for macros --- src/lay/lay/MacroPropertiesDialog.ui | 46 ++++++++++--- src/lay/lay/layMacroPropertiesDialog.cc | 5 ++ src/lym/lym/lymMacro.cc | 90 +++++++++++++++++++++---- src/lym/lym/lymMacro.h | 15 +++++ 4 files changed, 134 insertions(+), 22 deletions(-) diff --git a/src/lay/lay/MacroPropertiesDialog.ui b/src/lay/lay/MacroPropertiesDialog.ui index d7a2c29e8..1a281dac4 100644 --- a/src/lay/lay/MacroPropertiesDialog.ui +++ b/src/lay/lay/MacroPropertiesDialog.ui @@ -100,7 +100,6 @@ - 75 true @@ -224,25 +223,56 @@ 6 + + + Priority + + + + Prolog - - - - - - - + Epilog + + + + + 0 + 0 + + + + + + + + for autorun: 0 = first, 1 = second ... + + + + + + + + 1 + 0 + + + + + + + diff --git a/src/lay/lay/layMacroPropertiesDialog.cc b/src/lay/lay/layMacroPropertiesDialog.cc index 58fcf16f5..2c04a77c8 100644 --- a/src/lay/lay/layMacroPropertiesDialog.cc +++ b/src/lay/lay/layMacroPropertiesDialog.cc @@ -75,6 +75,7 @@ MacroPropertiesDialog::update (const lym::Macro *macro) propertiesFrame->setEnabled (! macro->is_readonly ()); description->setText (tl::to_qstring (macro->description ())); version->setText (tl::to_qstring (macro->version ())); + priority->setText (tl::to_qstring (tl::to_string (macro->priority ()))); prolog->setText (tl::to_qstring (macro->prolog ())); epilog->setText (tl::to_qstring (macro->epilog ())); autorun->setChecked (macro->is_autorun ()); @@ -98,6 +99,10 @@ MacroPropertiesDialog::commit (lym::Macro *macro) macro->set_show_in_menu (showmenu->isChecked ()); macro->set_group_name (tl::to_string (groupName->text ())); macro->set_menu_path (tl::to_string (menuPath->text ())); + + int p = 0; + tl::from_string (tl::to_string (priority->text ()), p); + macro->set_priority (p); } } diff --git a/src/lym/lym/lymMacro.cc b/src/lym/lym/lymMacro.cc index f7e34fe36..429a4fbf2 100644 --- a/src/lym/lym/lymMacro.cc +++ b/src/lym/lym/lymMacro.cc @@ -55,7 +55,7 @@ namespace lym // ---------------------------------------------------------------------- Macro::Macro () - : m_modified (true), m_readonly (false), m_autorun (false), m_autorun_default (false), m_autorun_early (false), m_show_in_menu (false), m_is_file (false), mp_parent (0), m_interpreter (None), m_format (Macro::NoFormat) + : m_modified (true), m_readonly (false), m_autorun (false), m_autorun_default (false), m_autorun_early (false), m_priority (0), m_show_in_menu (false), m_is_file (false), mp_parent (0), m_interpreter (None), m_format (Macro::NoFormat) { // .. nothing yet .. } @@ -89,6 +89,7 @@ void Macro::assign (const lym::Macro &other) m_autorun = other.m_autorun; m_autorun_default = other.m_autorun_default; m_autorun_early = other.m_autorun_early; + m_priority = other.m_priority; m_show_in_menu = other.m_show_in_menu; m_shortcut = other.m_shortcut; m_format = other.m_format; @@ -113,6 +114,7 @@ bool Macro::operator== (const Macro &other) const m_text == other.m_text && m_autorun == other.m_autorun && m_autorun_early == other.m_autorun_early && + m_priority == other.m_priority && m_show_in_menu == other.m_show_in_menu && m_shortcut == other.m_shortcut && m_interpreter == other.m_interpreter && @@ -184,6 +186,7 @@ static tl::XMLStruct xml_struct ("klayout-macro", tl::make_member (&Macro::doc, &Macro::set_doc, "doc") + tl::make_member (&Macro::is_autorun, &Macro::set_autorun, "autorun") + tl::make_member (&Macro::is_autorun_early, &Macro::set_autorun_early, "autorun-early") + + tl::make_member (&Macro::priority, &Macro::set_priority, "priority") + tl::make_member (&Macro::shortcut, &Macro::set_shortcut, "shortcut") + tl::make_member (&Macro::show_in_menu, &Macro::set_show_in_menu, "show-in-menu") + tl::make_member (&Macro::group_name, &Macro::set_group_name, "group-name") + @@ -552,19 +555,22 @@ struct PropertyField void (lym::Macro::*string_setter) (const std::string &); bool (lym::Macro::*bool_getter) () const; void (lym::Macro::*bool_setter) (bool); + int (lym::Macro::*int_getter) () const; + void (lym::Macro::*int_setter) (int); }; static PropertyField property_fields[] = { - { "description", &lym::Macro::description, &lym::Macro::set_description, 0, 0 }, - { "prolog", &lym::Macro::prolog, &lym::Macro::set_prolog, 0, 0 }, - { "epilog", &lym::Macro::epilog, &lym::Macro::set_epilog, 0, 0 }, - { "version", &lym::Macro::version, &lym::Macro::set_version, 0, 0 }, - { "autorun", 0, 0, &lym::Macro::is_autorun, &lym::Macro::set_autorun }, - { "autorun-early", 0, 0, &lym::Macro::is_autorun_early, &lym::Macro::set_autorun_early}, - { "show-in-menu", 0, 0, &lym::Macro::show_in_menu, &lym::Macro::set_show_in_menu }, - { "group-name", &lym::Macro::group_name, &lym::Macro::set_group_name, 0, 0 }, - { "menu-path", &lym::Macro::menu_path, &lym::Macro::set_menu_path, 0, 0 }, - { "shortcut", &lym::Macro::shortcut, &lym::Macro::set_shortcut, 0, 0 } + { "description", &lym::Macro::description, &lym::Macro::set_description, 0, 0, 0, 0 }, + { "prolog", &lym::Macro::prolog, &lym::Macro::set_prolog, 0, 0, 0, 0 }, + { "epilog", &lym::Macro::epilog, &lym::Macro::set_epilog, 0, 0, 0, 0 }, + { "version", &lym::Macro::version, &lym::Macro::set_version, 0, 0, 0, 0 }, + { "autorun", 0, 0, &lym::Macro::is_autorun, &lym::Macro::set_autorun, 0, 0 }, + { "autorun-early", 0, 0, &lym::Macro::is_autorun_early, &lym::Macro::set_autorun_early, 0, 0 }, + { "show-in-menu", 0, 0, &lym::Macro::show_in_menu, &lym::Macro::set_show_in_menu, 0, 0 }, + { "group-name", &lym::Macro::group_name, &lym::Macro::set_group_name, 0, 0, 0, 0 }, + { "menu-path", &lym::Macro::menu_path, &lym::Macro::set_menu_path, 0, 0, 0, 0 }, + { "shortcut", &lym::Macro::shortcut, &lym::Macro::set_shortcut, 0, 0, 0, 0 }, + { "priority", 0, 0, 0, 0, &lym::Macro::priority, &lym::Macro::set_priority } }; static std::string escape_pta_string (const char *cp) @@ -625,6 +631,11 @@ void Macro::sync_text_with_properties () if (v) { new_lines.push_back (std::string ("# $") + pf->name); } + } else if (pf->int_getter) { + int v = (this->*(pf->int_getter)) (); + if (v) { + new_lines.push_back (std::string ("# $") + pf->name + ": " + tl::to_string (v)); + } } } @@ -672,6 +683,8 @@ void Macro::sync_properties_with_text () (this->*(pf->string_setter)) (std::string ()); } else if (pf->bool_setter) { (this->*(pf->bool_setter)) (false); + } else if (pf->int_setter) { + (this->*(pf->int_setter)) (0); } } @@ -696,6 +709,10 @@ void Macro::sync_properties_with_text () (this->*(pf->string_setter)) (unescape_pta_string (pex.skip ())); } else if (pf->bool_setter) { (this->*(pf->bool_setter)) (true); + } else if (pf->int_setter) { + int v = 0; + tl::from_string (pex.skip (), v); + (this->*(pf->int_setter)) (v); } break; @@ -767,6 +784,15 @@ void Macro::set_autorun (bool f) } } +void Macro::set_priority (int p) +{ + if (p != m_priority) { + m_modified = true; + m_priority = p; + on_changed (); + } +} + void Macro::set_show_in_menu (bool f) { if (f != m_show_in_menu) { @@ -1875,15 +1901,38 @@ bool MacroCollection::has_autorun_early () const return has_autorun_for (*this, true); } -static void autorun_for (lym::MacroCollection &collection, bool early, std::set *executed_already) +static int collect_priority (lym::MacroCollection &collection, bool early, int from_prio) +{ + int p = -1; + + for (lym::MacroCollection::child_iterator c = collection.begin_children (); c != collection.end_children (); ++c) { + int pp = collect_priority (*c->second, early, from_prio); + if (pp >= from_prio && (p < 0 || pp < p)) { + p = pp; + } + } + + for (lym::MacroCollection::iterator c = collection.begin (); c != collection.end (); ++c) { + if (c->second->can_run () && ((early && c->second->is_autorun_early ()) || (!early && c->second->is_autorun () && !c->second->is_autorun_early ()))) { + int pp = c->second->priority (); + if (pp >= from_prio && (p < 0 || pp < p)) { + p = pp; + } + } + } + + return p; +} + +static void autorun_for_prio (lym::MacroCollection &collection, bool early, std::set *executed_already, int prio) { for (lym::MacroCollection::child_iterator c = collection.begin_children (); c != collection.end_children (); ++c) { - autorun_for (*c->second, early, executed_already); + autorun_for_prio (*c->second, early, executed_already, prio); } for (lym::MacroCollection::iterator c = collection.begin (); c != collection.end (); ++c) { - if (c->second->can_run () && ((early && c->second->is_autorun_early ()) || (!early && c->second->is_autorun () && !c->second->is_autorun_early ()))) { + if (c->second->priority () == prio && c->second->can_run () && ((early && c->second->is_autorun_early ()) || (!early && c->second->is_autorun () && !c->second->is_autorun_early ()))) { if (!executed_already || executed_already->find (c->second->path ()) == executed_already->end ()) { @@ -1903,6 +1952,19 @@ static void autorun_for (lym::MacroCollection &collection, bool early, std::set< } } +static void autorun_for (lym::MacroCollection &collection, bool early, std::set *executed_already) +{ + int prio = 0; + while (true) { + int p = collect_priority (collection, early, prio); + if (p < prio) { + break; + } + autorun_for_prio (collection, early, executed_already, p); + prio = p + 1; + } +} + void MacroCollection::autorun (std::set *already_executed) { autorun_for (*this, false, already_executed); diff --git a/src/lym/lym/lymMacro.h b/src/lym/lym/lymMacro.h index 063901ca4..619be68a6 100644 --- a/src/lym/lym/lymMacro.h +++ b/src/lym/lym/lymMacro.h @@ -430,6 +430,20 @@ public: */ void set_autorun_early (bool f); + /** + * @brief Gets the priority of the macro in autorun and autorun-early mode + * 0 is the first priority, -1 means "never execute". + */ + int priority () const + { + return m_priority; + } + + /** + * @brief Sets the priority + */ + void set_priority (int p); + /** * @brief Gets a value indicating whether the macro shall be shown in the menu */ @@ -596,6 +610,7 @@ private: bool m_autorun; bool m_autorun_default; bool m_autorun_early; + int m_priority; bool m_show_in_menu; std::string m_group_name; std::string m_menu_path; From d0d3d2a8beab0dc89f60bbe6769bb572b2f79718 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 24 Feb 2022 00:45:02 +0100 Subject: [PATCH 08/24] New integration, WIP --- .../lay_plugin/built-in-macros/_d25_engine.rb | 249 ++++++++++++++++++ .../built-in-macros/d25_install.lym | 51 ++++ .../built-in-macros/d25_interpreters.lym | 170 ++++++++++++ .../view_25d/lay_plugin/layD25Resources.qrc | 10 + .../tools/view_25d/lay_plugin/lay_plugin.pro | 3 + .../view_25d/lay_plugin/templates/d25.lym | 59 +++++ 6 files changed, 542 insertions(+) create mode 100644 src/plugins/tools/view_25d/lay_plugin/built-in-macros/_d25_engine.rb create mode 100644 src/plugins/tools/view_25d/lay_plugin/built-in-macros/d25_install.lym create mode 100644 src/plugins/tools/view_25d/lay_plugin/built-in-macros/d25_interpreters.lym create mode 100644 src/plugins/tools/view_25d/lay_plugin/layD25Resources.qrc create mode 100644 src/plugins/tools/view_25d/lay_plugin/templates/d25.lym diff --git a/src/plugins/tools/view_25d/lay_plugin/built-in-macros/_d25_engine.rb b/src/plugins/tools/view_25d/lay_plugin/built-in-macros/_d25_engine.rb new file mode 100644 index 000000000..591ea4085 --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/built-in-macros/_d25_engine.rb @@ -0,0 +1,249 @@ +# $autorun-early +# $priority: 1 + +require 'pathname' + +module D25 + + class D25ZInfo + + attr_accessor :layer, :zstart, :zstop, :display + + def initialize(_layer, _zstart, _zstop, _display) + self.layer = _layer + self.zstart = _zstart + self.zstop = _zstop + self.display = _display + end + + end + + class D25Display + + attr_accessor :fill, :frame, :like + + def initialize + self.fill = nil + self.frame = nil + self.like = nil + end + + end + + # The D25 engine + + class D25Engine < DRC::DRCEngine + + def initialize + + super + + @current_z = 0.0 + @zstack = [] + + # clip to layout view + if RBA::LayoutView::current + self.clip(RBA::LayoutView::current.box) + end + + end + + def z(*args) + + self._context("z") do + + layer = nil + zstart = nil + zstop = nil + height = nil + + args.each do |a| + + if a.is_a?(Range) + + zstart = a.min + zstop = a.max + + elsif a.is_a?(DRC::DRCLayer) + + if layer + raise("Duplicate layer argument") + end + layer = a + + elsif a.is_a?(1.class) || a.is_a?(1.0.class) + + if height + raise("Duplicate height specification") + end + height = a + + elsif a.is_a?(Hash) + + if a[:height] + if height + raise("Duplicate height specification") + end + height = a[:height] + end + if a[:zstart] + if zstart + raise("Duplicate zstart specification") + end + zstart = a[:zstart] + end + if a[:zstop] + if zstop + raise("Duplicate zstop specification") + end + zstop = a[:zstop] + end + invalid_keys = a.keys.select { |k| ![ :height, :zstart, :zstop ].member?(k) } + if invalid_keys.size > 0 + raise("Keyword argument(s) not understood: #{invalid_keys.collect(&:to_s).join(',')}") + end + + else + raise("Argument not understood: #{a.inspect}") + end + + end + + if ! zstart + zstart = @current_z + end + if ! zstop && ! height + raise("Either height or zstop must be specified") + elsif zstop && height + raise("Either height or zstop must be specified, not both") + end + if height + zstop = zstart + height + end + @current_z = zstop + + if ! layer + raise("No layer specified") + end + + info = D25ZInfo::new(layer.data, zstart, zstop, @display || D25Display::new) + @zstack << info + + return info + + end + + end + + def display(*args, &block) + + display = D25Display::new + + args.each do |a| + + if a.is_a?(D25ZInfo) + + @zstack.each do |z| + if z == a + z.display = display + end + end + + elsif a.is_a?(Hash) + + hollow_fill = 0xffffffff + + if a[:color] + if !a[:color].is_a?(0xffffff.class) + raise("'color' must be a color value (an integer)") + end + display.fill = a[:color] + display.frame = a[:color] + end + if a[:frame] + if !a[:frame].is_a?(0xffffff.class) + raise("'frame' must be a color value (an integer)") + end + display.frame = a[:frame] + end + if a[:fill] + if !a[:fill].is_a?(0xffffff.class) + raise("'fill' must be a color value (an integer)") + end + display.fill = a[:fill] + end + if a[:hollow] + if a[:hollow] + display.fill = hollow_fill + end + end + + if a[:like] + li = nil + if a[:like].is_a?(String) + li = RBA::LayerInfo::from_string(a[:like]) + elsif a[:like].is_a?(RBA::LayerInfo) + li = a[:like] + else + raise("'like' must be a string or LayerInfo object") + end + display.like = li + end + + invalid_keys = a.keys.select { |k| ![ :fill, :frame, :color, :hollow, :like ].member?(k) } + if invalid_keys.size > 0 + raise("Keyword argument(s) not understood: #{invalid_keys.collect(&:to_s).join(',')}") + end + + else + raise("Argument not understood: #{a.inspect}") + end + + end + + end + + def _finish(final = true) + + if final && @zstack.empty? + raise("No z calls made in 2.5d script") + end + + super(final) + + if final + + view = RBA::LayoutView::current.open_d25_view + + begin + + displays = {} + + @zstack.each do |z| + (displays[z.display.object_id] ||= []) << z + end + + displays.each do |k,zz| + display = zz[0].display + view.open_display(display.frame, display.fill, display.like) + zz.each do |z| + view.entry(z.layer, z.start, z.zstop) + end + view.close_display + end + + view.finish + + rescue => ex + view.close + raise ex + end + + end + + end + + end + +end + diff --git a/src/plugins/tools/view_25d/lay_plugin/built-in-macros/d25_install.lym b/src/plugins/tools/view_25d/lay_plugin/built-in-macros/d25_install.lym new file mode 100644 index 000000000..6e0ebc18f --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/built-in-macros/d25_install.lym @@ -0,0 +1,51 @@ + + + + + + + + + true + false + + false + + + ruby + + +module D25 + + # Installs the home menu entries (needs to be done on autorun, not autorun-early) + + if RBA::Application::instance && RBA::Application::instance.main_window + + cat = "d25" + name = "2.5d" + + mw = RBA::Application::instance.main_window + mw.menu.insert_menu("tools_menu.verification_group+", "d25", "2.5d View") + + @new_action = RBA::Action::new + @new_action.title = "New #{name} Script" + @new_action.on_triggered do + mw.show_macro_editor(cat, true) + end + + mw.menu.insert_item("tools_menu.#{cat}.end", "new_script", @new_action) + + @edit_action = RBA::Action::new + @edit_action.title = "Edit #{name} Script" + @edit_action.on_triggered do + mw.show_macro_editor(cat, false) + end + + mw.menu.insert_item("tools_menu.#{cat}.end", "edit_script", @edit_action) + + end + +end + + + diff --git a/src/plugins/tools/view_25d/lay_plugin/built-in-macros/d25_interpreters.lym b/src/plugins/tools/view_25d/lay_plugin/built-in-macros/d25_interpreters.lym new file mode 100644 index 000000000..fbf0dc9ef --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/built-in-macros/d25_interpreters.lym @@ -0,0 +1,170 @@ + + + + + + + + + false + true + + false + + + ruby + + +module D25 + + class D25Executable < RBA::Executable + + def initialize(macro, generator, rdb_index = nil) + + @d25 = D25Engine::new + @d25._rdb_index = rdb_index + @d25._generator = generator + + @macro = macro + + end + + def execute + + @d25._start("D25: " + @macro.path) + + # Set a debugger scope so that our errors end up with the debugger set to the D25's line + RBA::MacroExecutionContext::set_debugger_scope(@macro.path) + + begin + + # No verbosity set in d25 engine - we cannot use the engine's logger + RBA::Logger::verbosity >= 10 && RBA::Logger::info("Running #{@macro.path}") + @d25.instance_eval(@macro.text, @macro.path) + + rescue => ex + + @d25.error("In #{@macro.path}: #{ex.to_s}") + RBA::MacroExecutionContext::ignore_next_exception + raise ex + + end + + nil + + end + + def cleanup + + # Remove the debugger scope + RBA::MacroExecutionContext::remove_debugger_scope + + # cleans up and creates layout and report views + @d25._finish + + end + + end + + # A DSL implementation for a D25 language (XML format) + class D25Interpreter < RBA::MacroInterpreter + + # Constructor + def initialize(recipe) + + @recipe = recipe + + # Make the DSL use ruby syntax highlighting + self.syntax_scheme = "ruby" + self.suffix = "lyd25" + self.debugger_scheme = RBA::MacroInterpreter::RubyDebugger + self.storage_scheme = RBA::MacroInterpreter::MacroFormat + self.description = "D25" + + # Registers the new interpreter + register("d25-dsl-xml") + + # create a template for the macro editor: + create_template(":/d25-templates/d25.lym") + + # if available, create a menu branch + if RBA::Application::instance && RBA::Application::instance.main_window + mw = RBA::Application::instance.main_window + mw.menu.insert_menu("tools_menu.verification_group+", "d25", "2.5d View") + end + + end + + # Implements the execute method + def executable(macro) + D25Executable::new(macro, @recipe.generator("script" => macro.path)) + end + + end + + # A DSL implementation for a D25 language (Plain text format) + class D25PlainTextInterpreter < RBA::MacroInterpreter + + # Constructor + def initialize(recipe) + + @recipe = recipe + + # Make the DSL use ruby syntax highlighting + self.syntax_scheme = "ruby" + self.suffix = "d25" + self.debugger_scheme = RBA::MacroInterpreter::RubyDebugger + self.storage_scheme = RBA::MacroInterpreter::PlainTextWithHashAnnotationsFormat + self.description = "D25 (Text)" + + # Registers the new interpreter + register("d25-dsl") + + end + + # Implements the execute method + def executable(macro) + D25Executable::new(macro, @recipe.generator("script" => macro.path)) + end + + end + + # A recipe implementation allowing the D25 run to be redone + class D25Recipe < RBA::Recipe + + def initialize + super("d25", "D25 recipe") + end + + def executable(params) + + script = params["script"] + if ! script + return + end + + macro = RBA::Macro::macro_by_path(script) + macro || raise("Can't find D25 script #{script} - unable to re-run") + + D25Executable::new(macro, self.generator("script" => script), params["rdb_index"]) + + end + + end + + # Register the recipe + d25_recipe = D25Recipe::new + + # Register the new interpreters + D25Interpreter::new(d25_recipe) + D25PlainTextInterpreter::new(d25_recipe) + + # Creates a new macro category + if RBA::Application::instance + RBA::Application::instance.add_macro_category("d25", "2.5d view", [ "d25" ]) + end + +end + + + diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25Resources.qrc b/src/plugins/tools/view_25d/lay_plugin/layD25Resources.qrc new file mode 100644 index 000000000..be37f46fe --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/layD25Resources.qrc @@ -0,0 +1,10 @@ + + + built-in-macros/_d25_engine.rb + built-in-macros/d25_interpreters.lym + built-in-macros/d25_install.lym + + + templates/d25.lym + + diff --git a/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro b/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro index b05f93e94..cc6063840 100644 --- a/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro +++ b/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro @@ -26,6 +26,9 @@ SOURCES = \ FORMS = \ D25View.ui \ +RESOURCES = \ + layD25Resources.qrc \ + greaterThan(QT_MAJOR_VERSION, 5) { QT += openglwidgets } diff --git a/src/plugins/tools/view_25d/lay_plugin/templates/d25.lym b/src/plugins/tools/view_25d/lay_plugin/templates/d25.lym new file mode 100644 index 000000000..ac0468824 --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/templates/d25.lym @@ -0,0 +1,59 @@ + + + General;;2.5d view generator script (*.lyd25)\nA script generating a 2.5d view + + d25 + + + + false + false + + true + d25_scripts + tools_menu.d25.end + ruby + + +# Read about 2.5d generator scripts in the User Manual in "Various Topics/The 2.5d View" + +# The script utilizes the DRC language with these two extensions +# +# z(layer, options ...): +# +# This function generates a extruded view from the given layer. +# The "options" describe the z extension. Valid forms are: +# +# z(layer, 0.1 .. 0.2) extrude layer to z = 0.1 to 0.2 um +# z(layer, zstart: 0.1, zstop: 0.2) same as above +# z(layer, zstart: 0.1, height: 0.1) same as above, but with height instead of zstop +# z(layer, height: 200.nm) extrude layer from last z position with a height of 200nm +# +# If layer is an original layer, the display options (colors, hollow) are taken +# from the original layer's display style. Otherwise KLayout decides about the +# initial display options. Use "display(...)" to control the display options. +# +# display(options) { block } +# display(z(...), z(...), ..., options): +# +# Specify the display options to use for the layers generated inside the block +# (which must contains at least one "z" call) or the given z calls: +# +# Options are: +# +# display(..., color: 0xff0000) use bright red for the material color (RGB) +# display(..., frame: 0xff0000) use bright red for the frame color (combine with "color" for the fill) +# display(..., hollow: true) use hollow style (only frame is drawn) +# display(..., like: "7/0") borrow style from layout view's style for layer "7/0" +# + +z(input(1, 0), 0.1 .. 0.2) + +display(like: 7/0) do + z(input(7, 0), height: 100.nm) + z(input(8, 0), height: 150.nm) +end + + + + From c030c844bb5997bee2516f4db73fd4959a06852a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 27 Feb 2022 21:33:26 +0100 Subject: [PATCH 09/24] Doc update --- .../view_25d/lay_plugin/built-in-macros/d25_interpreters.lym | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/tools/view_25d/lay_plugin/built-in-macros/d25_interpreters.lym b/src/plugins/tools/view_25d/lay_plugin/built-in-macros/d25_interpreters.lym index fbf0dc9ef..e85d1a45f 100644 --- a/src/plugins/tools/view_25d/lay_plugin/built-in-macros/d25_interpreters.lym +++ b/src/plugins/tools/view_25d/lay_plugin/built-in-macros/d25_interpreters.lym @@ -161,7 +161,7 @@ module D25 # Creates a new macro category if RBA::Application::instance - RBA::Application::instance.add_macro_category("d25", "2.5d view", [ "d25" ]) + RBA::Application::instance.add_macro_category("d25", "2.5d View", [ "d25" ]) end end From 64406522fe27474458acd52025c32055eb39e6cc Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 5 Mar 2022 19:42:57 +0100 Subject: [PATCH 10/24] WIP: basic debugging, functionality. --- src/drc/drc/built-in-macros/_drc_engine.rb | 48 ++++ src/laybasic/laybasic/layParsedLayerSource.h | 2 +- .../tools/view_25d/lay_plugin/D25View.ui | 39 +++- .../lay_plugin/built-in-macros/_d25_engine.rb | 126 ++++++----- .../built-in-macros/d25_interpreters.lym | 2 + .../view_25d/lay_plugin/gsiDeclLayD25View.cc | 85 ++++++++ .../tools/view_25d/lay_plugin/layD25View.cc | 100 +++++++-- .../tools/view_25d/lay_plugin/layD25View.h | 15 ++ .../view_25d/lay_plugin/layD25ViewWidget.cc | 205 +++++++++++++++--- .../view_25d/lay_plugin/layD25ViewWidget.h | 48 ++-- .../tools/view_25d/lay_plugin/lay_plugin.pro | 1 + 11 files changed, 543 insertions(+), 128 deletions(-) create mode 100644 src/plugins/tools/view_25d/lay_plugin/gsiDeclLayD25View.cc diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index 3600355ca..9bf0556a0 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -1626,6 +1626,54 @@ CODE nil end + # %DRC% + # @name region_touch + # @brief Specifies region selected input in "touch mode" + # @synopsis region_touch(args) + # See \Source#touching for a description of that function. + # + # The following code will select shapes touching a 500x600 micron rectangle (lower left corner at 0,0) + # from the input layout. The shapes will not be clipped: + # + # @code + # region_touch(0.mm, 0.mm, 0.5.mm, 0.6.mm) + # # shapes will now be the ones touching the rectangular region + # l1 = input(1, 0) + # @/code + # + # To remove this condition, call "region_touch" without any arguments. + + def region_touch(*args) + self._context("region_touch") do + @def_source = layout.touching(*args) + end + nil + end + + # %DRC% + # @name region_overlap + # @brief Specifies region selected input in "overlap mode" + # @synopsis region_overlap(args) + # See \Source#overlapping for a description of that function. + # + # The following code will select shapes overlapping a 500x600 micron rectangle (lower left corner at 0,0) + # from the input layout. The shapes will not be clipped: + # + # @code + # region_overlapping(0.mm, 0.mm, 0.5.mm, 0.6.mm) + # # shapes will now be the ones overlapping the rectangular region + # l1 = input(1, 0) + # @/code + # + # To remove this condition, call "region_overlapping" without any arguments. + + def region_overlap(*args) + self._context("region_overlap") do + @def_source = layout.overlapping(*args) + end + nil + end + # %DRC% # @name global_transform # @brief Gets or sets a global transformation diff --git a/src/laybasic/laybasic/layParsedLayerSource.h b/src/laybasic/laybasic/layParsedLayerSource.h index 7bc862d42..e3bbfa511 100644 --- a/src/laybasic/laybasic/layParsedLayerSource.h +++ b/src/laybasic/laybasic/layParsedLayerSource.h @@ -35,8 +35,8 @@ namespace db { - struct LayerProperties; class Layout; + struct LayerProperties; } namespace lay diff --git a/src/plugins/tools/view_25d/lay_plugin/D25View.ui b/src/plugins/tools/view_25d/lay_plugin/D25View.ui index 75301397d..056823f0c 100644 --- a/src/plugins/tools/view_25d/lay_plugin/D25View.ui +++ b/src/plugins/tools/view_25d/lay_plugin/D25View.ui @@ -358,7 +358,44 @@ 0 - + + + QFrame::NoFrame + + + QFrame::Plain + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + + 1 + 0 + + + + + + + + diff --git a/src/plugins/tools/view_25d/lay_plugin/built-in-macros/_d25_engine.rb b/src/plugins/tools/view_25d/lay_plugin/built-in-macros/_d25_engine.rb index 591ea4085..5af1d8468 100644 --- a/src/plugins/tools/view_25d/lay_plugin/built-in-macros/_d25_engine.rb +++ b/src/plugins/tools/view_25d/lay_plugin/built-in-macros/_d25_engine.rb @@ -42,9 +42,11 @@ module D25 @zstack = [] # clip to layout view - if RBA::LayoutView::current - self.clip(RBA::LayoutView::current.box) - end + if ! RBA::LayoutView::current + raise "No layout loaded for running 2.5d view on" + end + + self.region_overlap(RBA::LayoutView::current.box) end @@ -126,7 +128,7 @@ module D25 raise("No layer specified") end - info = D25ZInfo::new(layer.data, zstart, zstop, @display || D25Display::new) + info = D25ZInfo::new(layer, zstart, zstop, @display || D25Display::new) @zstack << info return info @@ -137,78 +139,91 @@ module D25 def display(*args, &block) - display = D25Display::new + begin - args.each do |a| + display = D25Display::new + @display = display - if a.is_a?(D25ZInfo) + args.each do |a| - @zstack.each do |z| - if z == a - z.display = display + if a.is_a?(D25ZInfo) + + @zstack.each do |z| + if z == a + z.display = display + end end - end - elsif a.is_a?(Hash) + elsif a.is_a?(Hash) - hollow_fill = 0xffffffff + hollow_fill = 0xffffffff - if a[:color] - if !a[:color].is_a?(0xffffff.class) - raise("'color' must be a color value (an integer)") + if a[:color] + if !a[:color].is_a?(0xffffff.class) + raise("'color' must be a color value (an integer)") + end + display.fill = a[:color] + display.frame = a[:color] end - display.fill = a[:color] - display.frame = a[:color] - end - if a[:frame] - if !a[:frame].is_a?(0xffffff.class) - raise("'frame' must be a color value (an integer)") + if a[:frame] + if !a[:frame].is_a?(0xffffff.class) + raise("'frame' must be a color value (an integer)") + end + display.frame = a[:frame] end - display.frame = a[:frame] - end - if a[:fill] - if !a[:fill].is_a?(0xffffff.class) - raise("'fill' must be a color value (an integer)") + if a[:fill] + if !a[:fill].is_a?(0xffffff.class) + raise("'fill' must be a color value (an integer)") + end + display.fill = a[:fill] end - display.fill = a[:fill] - end - if a[:hollow] if a[:hollow] - display.fill = hollow_fill + if a[:hollow] + display.fill = hollow_fill + end end - end - if a[:like] - li = nil - if a[:like].is_a?(String) - li = RBA::LayerInfo::from_string(a[:like]) - elsif a[:like].is_a?(RBA::LayerInfo) - li = a[:like] - else - raise("'like' must be a string or LayerInfo object") + if a[:like] + li = nil + if a[:like].is_a?(String) + li = RBA::LayerInfo::from_string(a[:like]) + elsif a[:like].is_a?(RBA::LayerInfo) + li = a[:like] + else + raise("'like' must be a string or LayerInfo object") + end + display.like = li end - display.like = li - end - invalid_keys = a.keys.select { |k| ![ :fill, :frame, :color, :hollow, :like ].member?(k) } - if invalid_keys.size > 0 - raise("Keyword argument(s) not understood: #{invalid_keys.collect(&:to_s).join(',')}") - end + invalid_keys = a.keys.select { |k| ![ :fill, :frame, :color, :hollow, :like ].member?(k) } + if invalid_keys.size > 0 + raise("Keyword argument(s) not understood: #{invalid_keys.collect(&:to_s).join(',')}") + end - else - raise("Argument not understood: #{a.inspect}") + else + raise("Argument not understood: #{a.inspect}") + end + end - + + yield + + ensure + @display = nil + end + + end + + def _check + + if @zstack.empty? + raise("No z calls made in 2.5d script") end end def _finish(final = true) - if final && @zstack.empty? - raise("No z calls made in 2.5d script") - end - super(final) if final @@ -217,6 +232,8 @@ module D25 begin + view.clear + displays = {} @zstack.each do |z| @@ -227,7 +244,7 @@ module D25 display = zz[0].display view.open_display(display.frame, display.fill, display.like) zz.each do |z| - view.entry(z.layer, z.start, z.zstop) + view.entry(z.layer.data, self.dbu, z.zstart, z.zstop) end view.close_display end @@ -235,6 +252,7 @@ module D25 view.finish rescue => ex + view.clear view.close raise ex end diff --git a/src/plugins/tools/view_25d/lay_plugin/built-in-macros/d25_interpreters.lym b/src/plugins/tools/view_25d/lay_plugin/built-in-macros/d25_interpreters.lym index e85d1a45f..368ad0080 100644 --- a/src/plugins/tools/view_25d/lay_plugin/built-in-macros/d25_interpreters.lym +++ b/src/plugins/tools/view_25d/lay_plugin/built-in-macros/d25_interpreters.lym @@ -50,6 +50,8 @@ module D25 end + @d25._check + nil end diff --git a/src/plugins/tools/view_25d/lay_plugin/gsiDeclLayD25View.cc b/src/plugins/tools/view_25d/lay_plugin/gsiDeclLayD25View.cc new file mode 100644 index 000000000..8a728555b --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/gsiDeclLayD25View.cc @@ -0,0 +1,85 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2022 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 "gsiDecl.h" +#include "gsiDeclBasic.h" + +#include "layD25View.h" +#include "layLayoutView.h" + +#include "dbLayerProperties.h" +#include "dbRegion.h" + +#include + +#if defined(HAVE_QTBINDINGS) + +# include "gsiQtGuiExternals.h" +# include "gsiQtWidgetsExternals.h" + +FORCE_LINK_GSI_QTGUI +FORCE_LINK_GSI_QTWIDGETS // for Qt5 + +#else +# define QT_EXTERNAL_BASE(x) +#endif + +namespace gsi +{ + +static lay::D25View *open_d25_view (lay::LayoutView *view) +{ + return lay::D25View::open (view); +} + +ClassExt decl_LayoutViewExt ( + gsi::method_ext ("open_d25_view", &open_d25_view, + "@brief Opens the 2.5d view window and returns a reference to the D25View object.\n" + "This method has been introduced in version 0.28.\n" + ) +); + +Class decl_D25View (QT_EXTERNAL_BASE (QDialog) "lay", "D25View", + gsi::method ("clear", &lay::D25View::clear, + "@brief Clears all display entries in the view" + ) + + gsi::method ("open_display", &lay::D25View::open_display, gsi::arg ("frame_color"), gsi::arg ("fill_color"), gsi::arg ("like"), + "@brief Creates a new display group" + ) + + gsi::method ("entry", &lay::D25View::entry, gsi::arg ("data"), gsi::arg ("dbu"), gsi::arg ("zstart"), gsi::arg ("zstop"), + "@brief Creates a new display entry in the group opened with \\open_display" + ) + + gsi::method ("close_display", &lay::D25View::close_display, + "@brief Finishes the display group" + ) + + gsi::method ("finish", &lay::D25View::finish, + "@brief Finishes the view - call this after the display groups have been created" + ) + + gsi::method ("close", &lay::D25View::close, + "@brief Closes the view" + ), + "@brief The 2.5d View Dialog\n" + "\n" + "This class is used internally to implement the 2.5d feature.\n" +); + +} diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc index af10a8621..37aba4a81 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc @@ -43,19 +43,19 @@ D25View::D25View (lay::Dispatcher *root, lay::LayoutView *view) mp_ui->d25_view->setFocusPolicy (Qt::StrongFocus); mp_ui->d25_view->setFocus (); - connect (mp_ui->fit_back, SIGNAL (clicked ()), this, SLOT (fit_button_clicked ())); - connect (mp_ui->fit_front, SIGNAL (clicked ()), this, SLOT (fit_button_clicked ())); - connect (mp_ui->fit_left, SIGNAL (clicked ()), this, SLOT (fit_button_clicked ())); - connect (mp_ui->fit_right, SIGNAL (clicked ()), this, SLOT (fit_button_clicked ())); - connect (mp_ui->fit_top, SIGNAL (clicked ()), this, SLOT (fit_button_clicked ())); - connect (mp_ui->fit_bottom, SIGNAL (clicked ()), this, SLOT (fit_button_clicked ())); - connect (mp_ui->zoom_slider, SIGNAL (valueChanged (int)), this, SLOT (scale_slider_changed (int))); - connect (mp_ui->vzoom_slider, SIGNAL (valueChanged (int)), this, SLOT (vscale_slider_changed (int))); - connect (mp_ui->zoom_factor, SIGNAL (editingFinished ()), this, SLOT (scale_value_edited ())); - connect (mp_ui->vzoom_factor, SIGNAL (editingFinished ()), this, SLOT (vscale_value_edited ())); - connect (mp_ui->d25_view, SIGNAL (scale_factor_changed (double)), this, SLOT (scale_factor_changed (double))); - connect (mp_ui->d25_view, SIGNAL (vscale_factor_changed (double)), this, SLOT (vscale_factor_changed (double))); - connect (mp_ui->d25_view, SIGNAL (init_failed ()), this, SLOT (init_failed ())); + connect (mp_ui->fit_back, SIGNAL (clicked()), this, SLOT (fit_button_clicked())); + connect (mp_ui->fit_front, SIGNAL (clicked()), this, SLOT (fit_button_clicked())); + connect (mp_ui->fit_left, SIGNAL (clicked()), this, SLOT (fit_button_clicked())); + connect (mp_ui->fit_right, SIGNAL (clicked()), this, SLOT (fit_button_clicked())); + connect (mp_ui->fit_top, SIGNAL (clicked()), this, SLOT (fit_button_clicked())); + connect (mp_ui->fit_bottom, SIGNAL (clicked()), this, SLOT (fit_button_clicked())); + connect (mp_ui->zoom_slider, SIGNAL (valueChanged(int)), this, SLOT (scale_slider_changed(int))); + connect (mp_ui->vzoom_slider, SIGNAL (valueChanged(int)), this, SLOT (vscale_slider_changed(int))); + connect (mp_ui->zoom_factor, SIGNAL (editingFinished()), this, SLOT (scale_value_edited())); + connect (mp_ui->vzoom_factor, SIGNAL (editingFinished()), this, SLOT (vscale_value_edited())); + connect (mp_ui->d25_view, SIGNAL (scale_factor_changed(double)), this, SLOT (scale_factor_changed(double))); + connect (mp_ui->d25_view, SIGNAL (vscale_factor_changed(double)), this, SLOT (vscale_factor_changed(double))); + connect (mp_ui->d25_view, SIGNAL (init_failed()), this, SLOT (init_failed())); mp_ui->gl_stack->setCurrentIndex (0); @@ -84,7 +84,7 @@ D25View::cellviews_changed () void D25View::layer_properties_changed (int) { - mp_ui->d25_view->refresh_view (); + // @@@ mp_ui->d25_view->refresh_view (); } void @@ -113,6 +113,71 @@ D25View::menu_activated (const std::string &symbol) } } +D25View * +D25View::open (lay::LayoutView *view) +{ + D25View *d25_view = view->get_plugin (); + if (d25_view) { + + d25_view->show (); + d25_view->activateWindow (); + d25_view->raise (); + + try { + d25_view->activate (); + } catch (...) { + d25_view->deactivate (); + throw; + } + + } + + return d25_view; +} + +void +D25View::close () +{ + hide (); +} + +void +D25View::clear () +{ + mp_ui->d25_view->clear (); +} + +void +D25View::open_display (const color_t *frame_color, const color_t *fill_color, const db::LayerProperties *like) +{ + mp_ui->d25_view->open_display (frame_color, fill_color, like); +} + +void +D25View::close_display () +{ + mp_ui->d25_view->close_display (); +} + +void +D25View::entry (const db::Region &data, double dbu, double zstart, double zstop) +{ + mp_ui->d25_view->entry (data, dbu, zstart, zstop); +} + +void +D25View::finish () +{ + mp_ui->d25_view->finish (); + + // @@@ install + + mp_ui->d25_view->reset (); + mp_ui->d25_view->set_cam_azimuth (0.0); + mp_ui->d25_view->set_cam_elevation (-initial_elevation); + mp_ui->d25_view->fit (); +} + static QString scale_factor_to_string (double f) { return QString (QString::fromUtf8 ("%1")).arg (f, 0, 'g', 3); @@ -198,12 +263,7 @@ D25View::deactivated () void D25View::activated () { - bool any = mp_ui->d25_view->attach_view (view ()); - if (! any) { - mp_ui->d25_view->attach_view (0); - throw tl::Exception (tl::to_string (tr ("No z data configured for the layers in this view.\nUse \"Tools/Manage Technologies\" to set up a z stack or check if it applies to the layers here."))); - } - + mp_ui->d25_view->attach_view (view ()); mp_ui->d25_view->reset (); mp_ui->d25_view->set_cam_azimuth (0.0); mp_ui->d25_view->set_cam_elevation (-initial_elevation); diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.h b/src/plugins/tools/view_25d/lay_plugin/layD25View.h index a4e420576..9976b15cf 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25View.h +++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.h @@ -27,6 +27,7 @@ #include "tlObject.h" #include "layBrowser.h" +#include "layViewOp.h" namespace Ui { @@ -38,6 +39,12 @@ namespace lay class LayoutView; } +namespace db +{ + class Region; + struct LayerProperties; +} + namespace lay { @@ -54,6 +61,14 @@ public: virtual void deactivated (); virtual void activated (); + static D25View *open (lay::LayoutView *view); + void close (); + void clear (); + void open_display (const color_t *frame_color, const color_t *fill_color, const db::LayerProperties *like); + void close_display (); + void entry (const db::Region &data, double dbu, double zstart, double zstop); + void finish (); + protected: void accept (); void reject (); diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index 57812aeb6..5150dc008 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -31,6 +31,8 @@ #include "dbPolygonGenerators.h" #include "dbPolygonTools.h" #include "dbClip.h" +#include "dbRegion.h" +#include "dbOriginalLayerRegion.h" #include "tlException.h" #include "tlProgress.h" @@ -200,6 +202,8 @@ D25ViewWidget::D25ViewWidget (QWidget *parent) setFormat (format); m_zmin = m_zmax = 0.0; + m_zset = false; + m_display_open = false; mp_view = 0; reset_viewport (); @@ -496,17 +500,146 @@ D25ViewWidget::aspect_ratio () const return double (width ()) / double (height ()); } -bool +void +D25ViewWidget::clear () +{ + m_layers.clear (); + m_layer_to_info.clear (); + m_vertex_chunks.clear (); + m_line_chunks.clear (); + + m_zset = false; + m_zmin = m_zmax = 0.0; + m_display_open = false; + + if (! mp_view) { + m_bbox = db::DBox (-1.0, -1.0, 1.0, 1.0); + } else { + m_bbox = mp_view->viewport ().box (); + } +} + +static void color_to_gl (color_t color, GLfloat (&gl_color) [4]) +{ + gl_color[0] = ((color >> 16) & 0xff) / 255.0f; + gl_color[1] = ((color >> 8) & 0xff) / 255.0f; + gl_color[2] = (color & 0xff) / 255.0f; + gl_color[3] = 1.0f; +} + +static void color_to_gl (const color_t *color, GLfloat (&gl_color) [4]) +{ + if (! color) { + for (unsigned int i = 0; i < 4; ++i) { + gl_color [i] = 0.0; + } + } else { + color_to_gl (*color, gl_color); + } +} + +static void lp_to_info (const lay::LayerPropertiesNode &lp, D25ViewWidget::LayerInfo &info) +{ + color_to_gl (lp.fill_color (true), info.fill_color); + if (lp.dither_pattern (true) == 1 /*hollow*/) { + info.fill_color [3] = 0.0f; + } + + color_to_gl (lp.frame_color (true), info.frame_color); + if (lp.frame_color (true) == lp.fill_color (true) && info.fill_color [3] > 0.5) { + // optimize: don't draw wire frame unless required + info.frame_color [3] = 0.0f; + } + + info.visible = lp.visible (true); +} + +void +D25ViewWidget::open_display (const color_t *frame_color, const color_t *fill_color, const db::LayerProperties *like) +{ + m_vertex_chunks.push_back (triangle_chunks_type ()); + m_line_chunks.push_back (line_chunks_type ()); + + LayerInfo info; + + info.visible = true; + color_to_gl (frame_color, info.frame_color); + color_to_gl (fill_color, info.fill_color); + info.vertex_chunk = &m_vertex_chunks.back (); + info.line_chunk = &m_line_chunks.back (); + + if (like && mp_view) { + for (lay::LayerPropertiesConstIterator lp = mp_view->begin_layers (); ! lp.at_end (); ++lp) { + if (! lp->has_children () && lp->source (true).layer_props ().log_equal (*like)) { + lp_to_info (*lp, info); + break; + } + } + } + + m_layers.push_back (info); + m_display_open = true; +} + +void +D25ViewWidget::close_display () +{ + m_display_open = false; +} + +void +D25ViewWidget::entry (const db::Region &data, double dbu, double zstart, double zstop) +{ + tl_assert (m_display_open); + + // try to establish a default color from the region's origin if required + const db::OriginalLayerRegion *original_region = dynamic_cast (data.delegate ()); + if (mp_view && m_layers.back ().fill_color [3] == 0.0 && m_layers.back ().frame_color [3] == 0.0) { + + if (original_region) { + + const db::RecursiveShapeIterator *iter = original_region->iter (); + if (iter && iter->layout () && iter->layout ()->is_valid_layer (iter->layer ())) { + + db::LayerProperties like = iter->layout ()->get_properties (iter->layer ()); + + for (lay::LayerPropertiesConstIterator lp = mp_view->begin_layers (); ! lp.at_end (); ++lp) { + if (! lp->has_children () && lp->source (true).layer_props ().log_equal (like)) { + lp_to_info (*lp, m_layers.back ()); + break; + } + } + + } + + } else { + + // sequential assignment + lay::color_t color = mp_view->get_palette ().luminous_color_by_index (m_layers.size ()); + color_to_gl (color, m_layers.back ().frame_color); + color_to_gl (color, m_layers.back ().fill_color); + + } + + } + + tl::AbsoluteProgress progress (tl::to_string (tr ("Rendering ..."))); + render_region (progress, *m_layers.back ().vertex_chunk, *m_layers.back ().line_chunk, data, dbu, db::CplxTrans (dbu).inverted () * m_bbox, zstart, zstop); +} + +void +D25ViewWidget::finish () +{ + // @@@ +} + +void D25ViewWidget::attach_view (LayoutView *view) { mp_view = view; - - bool any = prepare_view (); - reset (); - - return any; } +#if 0 // @@@ namespace { class ZDataCache @@ -578,31 +711,6 @@ namespace { } -static void color_to_gl (color_t color, GLfloat (&gl_color) [4]) -{ - gl_color[0] = ((color >> 16) & 0xff) / 255.0f; - gl_color[1] = ((color >> 8) & 0xff) / 255.0f; - gl_color[2] = (color & 0xff) / 255.0f; - gl_color[3] = 1.0f; -} - -void -D25ViewWidget::lp_to_info (const lay::LayerPropertiesNode &lp, LayerInfo &info) -{ - color_to_gl (lp.fill_color (true), info.color); - if (lp.dither_pattern (true) == 1 /*hollow*/) { - info.color [3] = 0.0f; - } - - color_to_gl (lp.frame_color (true), info.frame_color); - if (lp.frame_color (true) == lp.fill_color (true) && info.color [3] > 0.5) { - // optimize: don't draw wire frame unless required - info.frame_color [3] = 0.0f; - } - - info.visible = lp.visible (true); -} - bool D25ViewWidget::prepare_view () { @@ -712,6 +820,9 @@ D25ViewWidget::refresh_view () refresh (); } +// ----------------------- +#endif + void D25ViewWidget::render_polygon (D25ViewWidget::triangle_chunks_type &chunks, D25ViewWidget::line_chunks_type &line_chunks, const db::Polygon &poly, double dbu, double zstart, double zstop) { @@ -792,6 +903,8 @@ D25ViewWidget::render_wall (D25ViewWidget::triangle_chunks_type &chunks, D25View line_chunks.add (edge.p1 ().x () * dbu, zstop, edge.p1 ().y () * dbu); } +// @@@ +#if 0 void D25ViewWidget::render_layout (tl::AbsoluteProgress &progress, D25ViewWidget::triangle_chunks_type &chunks, D25ViewWidget::line_chunks_type &line_chunks, const db::Layout &layout, const db::Cell &cell, const db::Box &clip_box, unsigned int layer, double zstart, double zstop) { @@ -824,6 +937,32 @@ D25ViewWidget::render_layout (tl::AbsoluteProgress &progress, D25ViewWidget::tri } } +#endif + +void +D25ViewWidget::render_region (tl::AbsoluteProgress &progress, D25ViewWidget::triangle_chunks_type &chunks, D25ViewWidget::line_chunks_type &line_chunks, const db::Region ®ion, double dbu, const db::Box &clip_box, double zstart, double zstop) +{ + std::vector poly_heap; + + for (db::Region::const_iterator p = region.begin (); !p.at_end (); ++p) { + + poly_heap.clear (); + db::clip_poly (*p, clip_box, poly_heap, false /*keep holes*/); + + for (std::vector::const_iterator p = poly_heap.begin (); p != poly_heap.end (); ++p) { + + ++progress; + + render_polygon (chunks, line_chunks, *p, dbu, zstart, zstop); + + for (db::Polygon::polygon_edge_iterator e = p->begin_edge (); ! e.at_end (); ++e) { + render_wall (chunks, line_chunks, *e, dbu, zstart, zstop); + } + + } + + } +} static std::pair find_grid (double v) { @@ -1098,8 +1237,8 @@ D25ViewWidget::paintGL () glEnableVertexAttribArray (positions); for (std::vector::const_iterator l = m_layers.begin (); l != m_layers.end (); ++l) { - if (l->visible && l->color [3] > 0.5) { - m_shapes_program->setUniformValue ("color", l->color [0], l->color [1], l->color [2], l->color [3]); + if (l->visible && l->fill_color [3] > 0.5) { + m_shapes_program->setUniformValue ("color", l->fill_color [0], l->fill_color [1], l->fill_color [2], l->fill_color [3]); l->vertex_chunk->draw_to (this, positions, GL_TRIANGLES); } } diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h index 63b069463..5b7879517 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h @@ -27,6 +27,7 @@ #include "layD25MemChunks.h" #include "layD25Camera.h" +#include "layViewOp.h" #include #include @@ -41,8 +42,8 @@ namespace db { - class Layout; - class Cell; + class Region; + struct LayerProperties; } namespace tl @@ -78,6 +79,18 @@ class D25ViewWidget Q_OBJECT public: + typedef lay::mem_chunks triangle_chunks_type; + typedef lay::mem_chunks line_chunks_type; + + struct LayerInfo + { + triangle_chunks_type *vertex_chunk; + line_chunks_type *line_chunk; + GLfloat fill_color [4]; + GLfloat frame_color [4]; + bool visible; + }; + D25ViewWidget (QWidget *parent); ~D25ViewWidget (); @@ -88,8 +101,8 @@ public: void mouseReleaseEvent (QMouseEvent *event); void mouseMoveEvent (QMouseEvent *event); - bool attach_view(lay::LayoutView *view); - void refresh_view (); + void attach_view(lay::LayoutView *view); + // @@@ void refresh_view (); QVector3D hit_point_with_scene(const QVector3D &line_dir); void refresh (); @@ -129,6 +142,12 @@ public: return m_error; } + void clear (); + void open_display (const color_t *frame_color, const color_t *fill_color, const db::LayerProperties *like); + void close_display (); + void entry (const db::Region &data, double dbu, double zstart, double zstop); + void finish (); + signals: void scale_factor_changed (double f); void vscale_factor_changed (double f); @@ -143,9 +162,6 @@ public slots: void fit (); private: - typedef lay::mem_chunks triangle_chunks_type; - typedef lay::mem_chunks line_chunks_type; - std::unique_ptr mp_mode; QOpenGLShaderProgram *m_shapes_program, *m_lines_program, *m_gridplane_program; std::string m_error; @@ -155,32 +171,26 @@ private: lay::LayoutView *mp_view; db::DBox m_bbox; double m_zmin, m_zmax; + bool m_zset; + bool m_display_open; std::list m_vertex_chunks; std::list m_line_chunks; - struct LayerInfo { - const triangle_chunks_type *vertex_chunk; - const line_chunks_type *line_chunk; - GLfloat color [4]; - GLfloat frame_color [4]; - bool visible; - }; - std::vector m_layers; - std::multimap, size_t> m_layer_to_info; + std::multimap, size_t> m_layer_to_info; // @@@ void initializeGL (); void paintGL (); void resizeGL (int w, int h); void do_initialize_gl (); - bool prepare_view(); - void render_layout (tl::AbsoluteProgress &progress, D25ViewWidget::triangle_chunks_type &vertex_chunks, D25ViewWidget::line_chunks_type &line_chunks, const db::Layout &layout, const db::Cell &cell, const db::Box &clip_box, unsigned int layer, double zstart, double zstop); + // @@@ bool prepare_view(); + // @@@ void render_layout (tl::AbsoluteProgress &progress, D25ViewWidget::triangle_chunks_type &vertex_chunks, D25ViewWidget::line_chunks_type &line_chunks, const db::Layout &layout, const db::Cell &cell, const db::Box &clip_box, unsigned int layer, double zstart, double zstop); + void render_region (tl::AbsoluteProgress &progress, D25ViewWidget::triangle_chunks_type &vertex_chunks, D25ViewWidget::line_chunks_type &line_chunks, const db::Region ®ion, double dbu, const db::Box &clip_box, double zstart, double zstop); void render_polygon (D25ViewWidget::triangle_chunks_type &vertex_chunks, D25ViewWidget::line_chunks_type &line_chunks, const db::Polygon &poly, double dbu, double zstart, double zstop); void render_wall (D25ViewWidget::triangle_chunks_type &vertex_chunks, D25ViewWidget::line_chunks_type &line_chunks, const db::Edge &poly, double dbu, double zstart, double zstop); void reset_viewport (); - static void lp_to_info (const lay::LayerPropertiesNode &lp, D25ViewWidget::LayerInfo &info); }; } diff --git a/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro b/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro index cc6063840..aeac20510 100644 --- a/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro +++ b/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro @@ -16,6 +16,7 @@ HEADERS = \ layD25Camera.h SOURCES = \ + gsiDeclLayD25View.cc \ layD25View.cc \ layD25ViewWidget.cc \ layD25Plugin.cc \ From 05c16c9024c6a2680e374f341be21ec1567c4c03 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 5 Mar 2022 19:56:14 +0100 Subject: [PATCH 11/24] WIP: 'open window' menu item --- .../lay_plugin/built-in-macros/d25_install.lym | 12 ++++++++++++ .../tools/view_25d/lay_plugin/layD25Plugin.cc | 5 ++--- src/plugins/tools/view_25d/lay_plugin/layD25View.cc | 2 +- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/plugins/tools/view_25d/lay_plugin/built-in-macros/d25_install.lym b/src/plugins/tools/view_25d/lay_plugin/built-in-macros/d25_install.lym index 6e0ebc18f..ead33cc20 100644 --- a/src/plugins/tools/view_25d/lay_plugin/built-in-macros/d25_install.lym +++ b/src/plugins/tools/view_25d/lay_plugin/built-in-macros/d25_install.lym @@ -43,6 +43,18 @@ module D25 mw.menu.insert_item("tools_menu.#{cat}.end", "edit_script", @edit_action) + @open_action = RBA::Action::new + @open_action.title = "Open Window" + @open_action.on_triggered do + if ! RBA::LayoutView::current + RBA::MessageBox::critical("Error", "No layout loaded for running 2.5d view on", RBA::MessageBox::Ok) + else + RBA::LayoutView::current.open_d25_view + end + end + + mw.menu.insert_item("tools_menu.#{cat}.end", "open_window", @open_action) + end end diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc b/src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc index e3a12080d..c6830127c 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25Plugin.cc @@ -52,10 +52,9 @@ public: return 0; } - virtual void get_menu_entries (std::vector &menu_entries) const + virtual void get_menu_entries (std::vector & /*menu_entries*/) const { - lay::PluginDeclaration::get_menu_entries (menu_entries); - menu_entries.push_back (lay::menu_item ("lay::d25_view", "d25_view:edit", "tools_menu.post_verification_group", tl::to_string (QObject::tr ("2.5d View - experimental")))); + // .. nothing yet .. } virtual bool configure (const std::string & /*name*/, const std::string & /*value*/) diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc index 37aba4a81..56d6567de 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc @@ -170,7 +170,7 @@ D25View::finish () { mp_ui->d25_view->finish (); - // @@@ install + // @@@ install layer properties widget mp_ui->d25_view->reset (); mp_ui->d25_view->set_cam_azimuth (0.0); From 8c0498cc4cd63b6ea931cb6fadc4e4c339613797 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 5 Mar 2022 23:52:41 +0100 Subject: [PATCH 12/24] WIP: Some bug fixes, ongoing implementation --- .../tools/view_25d/lay_plugin/D25View.ui | 47 +++++ .../lay_plugin/built-in-macros/_d25_engine.rb | 6 +- .../built-in-macros/d25_interpreters.lym | 5 +- .../view_25d/lay_plugin/gsiDeclLayD25View.cc | 5 + .../tools/view_25d/lay_plugin/layD25View.cc | 80 +++++-- .../tools/view_25d/lay_plugin/layD25View.h | 5 + .../view_25d/lay_plugin/layD25ViewWidget.cc | 196 +----------------- .../view_25d/lay_plugin/layD25ViewWidget.h | 6 + .../view_25d/lay_plugin/templates/d25.lym | 4 +- 9 files changed, 144 insertions(+), 210 deletions(-) diff --git a/src/plugins/tools/view_25d/lay_plugin/D25View.ui b/src/plugins/tools/view_25d/lay_plugin/D25View.ui index 056823f0c..d8e818856 100644 --- a/src/plugins/tools/view_25d/lay_plugin/D25View.ui +++ b/src/plugins/tools/view_25d/lay_plugin/D25View.ui @@ -59,6 +59,39 @@ 0 + + + + Execute script again + + + ... + + + + :/run.png:/run.png + + + true + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 10 + 20 + + + + @@ -456,6 +489,20 @@ + + + + + + In order to use the 2.5d view you will need a script which generates the view. + + + Qt::AlignCenter + + + + + diff --git a/src/plugins/tools/view_25d/lay_plugin/built-in-macros/_d25_engine.rb b/src/plugins/tools/view_25d/lay_plugin/built-in-macros/_d25_engine.rb index 5af1d8468..e4a0e6376 100644 --- a/src/plugins/tools/view_25d/lay_plugin/built-in-macros/_d25_engine.rb +++ b/src/plugins/tools/view_25d/lay_plugin/built-in-macros/_d25_engine.rb @@ -163,7 +163,7 @@ module D25 raise("'color' must be a color value (an integer)") end display.fill = a[:color] - display.frame = a[:color] + display.frame = nil end if a[:frame] if !a[:frame].is_a?(0xffffff.class) @@ -206,7 +206,7 @@ module D25 end - yield + block && yield ensure @display = nil @@ -232,7 +232,7 @@ module D25 begin - view.clear + view.begin(self._generator) displays = {} diff --git a/src/plugins/tools/view_25d/lay_plugin/built-in-macros/d25_interpreters.lym b/src/plugins/tools/view_25d/lay_plugin/built-in-macros/d25_interpreters.lym index 368ad0080..86d7d954d 100644 --- a/src/plugins/tools/view_25d/lay_plugin/built-in-macros/d25_interpreters.lym +++ b/src/plugins/tools/view_25d/lay_plugin/built-in-macros/d25_interpreters.lym @@ -19,10 +19,9 @@ module D25 class D25Executable < RBA::Executable - def initialize(macro, generator, rdb_index = nil) + def initialize(macro, generator) @d25 = D25Engine::new - @d25._rdb_index = rdb_index @d25._generator = generator @macro = macro @@ -148,7 +147,7 @@ module D25 macro = RBA::Macro::macro_by_path(script) macro || raise("Can't find D25 script #{script} - unable to re-run") - D25Executable::new(macro, self.generator("script" => script), params["rdb_index"]) + D25Executable::new(macro, self.generator("script" => script)) end diff --git a/src/plugins/tools/view_25d/lay_plugin/gsiDeclLayD25View.cc b/src/plugins/tools/view_25d/lay_plugin/gsiDeclLayD25View.cc index 8a728555b..0c771858a 100644 --- a/src/plugins/tools/view_25d/lay_plugin/gsiDeclLayD25View.cc +++ b/src/plugins/tools/view_25d/lay_plugin/gsiDeclLayD25View.cc @@ -62,6 +62,9 @@ Class decl_D25View (QT_EXTERNAL_BASE (QDialog) "lay", "D25View", gsi::method ("clear", &lay::D25View::clear, "@brief Clears all display entries in the view" ) + + gsi::method ("begin", &lay::D25View::begin, gsi::arg ("generator"), + "@brief Initiates delivery of display groups" + ) + gsi::method ("open_display", &lay::D25View::open_display, gsi::arg ("frame_color"), gsi::arg ("fill_color"), gsi::arg ("like"), "@brief Creates a new display group" ) + @@ -80,6 +83,8 @@ Class decl_D25View (QT_EXTERNAL_BASE (QDialog) "lay", "D25View", "@brief The 2.5d View Dialog\n" "\n" "This class is used internally to implement the 2.5d feature.\n" + "\n" + "This class has been introduced in version 0.28." ); } diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc index 56d6567de..7e9a79b1a 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc @@ -20,6 +20,8 @@ */ +#include "tlExceptions.h" +#include "tlRecipe.h" #include "layD25View.h" #include "layLayoutView.h" @@ -35,7 +37,8 @@ namespace lay const double initial_elevation = 15.0; D25View::D25View (lay::Dispatcher *root, lay::LayoutView *view) - : lay::Browser (root, view, "d25_view") + : lay::Browser (root, view, "d25_view"), + dm_rerun_macro (this, &D25View::rerun_macro) { mp_ui = new Ui::D25View (); mp_ui->setupUi (this); @@ -56,10 +59,14 @@ D25View::D25View (lay::Dispatcher *root, lay::LayoutView *view) connect (mp_ui->d25_view, SIGNAL (scale_factor_changed(double)), this, SLOT (scale_factor_changed(double))); connect (mp_ui->d25_view, SIGNAL (vscale_factor_changed(double)), this, SLOT (vscale_factor_changed(double))); connect (mp_ui->d25_view, SIGNAL (init_failed()), this, SLOT (init_failed())); + connect (mp_ui->rerun_button, SIGNAL (clicked()), this, SLOT (rerun_button_pressed())); - mp_ui->gl_stack->setCurrentIndex (0); + mp_ui->rerun_button->setEnabled (false); + + mp_ui->gl_stack->setCurrentIndex (2); lay::activate_help_links (mp_ui->doc_label); + lay::activate_help_links (mp_ui->empty_label); view->cellviews_changed_event.add (this, &D25View::cellviews_changed); view->layer_list_changed_event.add (this, &D25View::layer_properties_changed); @@ -144,38 +151,67 @@ D25View::close () void D25View::clear () { - mp_ui->d25_view->clear (); + if (! mp_ui->d25_view->has_error ()) { + mp_ui->gl_stack->setCurrentIndex (2); + mp_ui->d25_view->clear (); + } + + mp_ui->rerun_button->setEnabled (false); + m_generator.clear (); +} + +void +D25View::begin (const std::string &generator) +{ + clear (); + + if (! mp_ui->d25_view->has_error ()) { + m_generator = generator; + mp_ui->rerun_button->setEnabled (true); + } } void D25View::open_display (const color_t *frame_color, const color_t *fill_color, const db::LayerProperties *like) { - mp_ui->d25_view->open_display (frame_color, fill_color, like); + if (! mp_ui->d25_view->has_error ()) { + mp_ui->d25_view->open_display (frame_color, fill_color, like); + } } void D25View::close_display () { - mp_ui->d25_view->close_display (); + if (! mp_ui->d25_view->has_error ()) { + mp_ui->d25_view->close_display (); + } } void D25View::entry (const db::Region &data, double dbu, double zstart, double zstop) { - mp_ui->d25_view->entry (data, dbu, zstart, zstop); + if (! mp_ui->d25_view->has_error ()) { + mp_ui->d25_view->entry (data, dbu, zstart, zstop); + } } void D25View::finish () { - mp_ui->d25_view->finish (); + if (! mp_ui->d25_view->has_error ()) { - // @@@ install layer properties widget + mp_ui->d25_view->finish (); - mp_ui->d25_view->reset (); - mp_ui->d25_view->set_cam_azimuth (0.0); - mp_ui->d25_view->set_cam_elevation (-initial_elevation); - mp_ui->d25_view->fit (); + // @@@ install layer properties widget + + mp_ui->d25_view->reset (); + mp_ui->d25_view->set_cam_azimuth (0.0); + mp_ui->d25_view->set_cam_elevation (-initial_elevation); + mp_ui->d25_view->fit (); + + mp_ui->gl_stack->setCurrentIndex (0); + + } } static QString scale_factor_to_string (double f) @@ -270,6 +306,26 @@ D25View::activated () mp_ui->d25_view->fit (); } +void +D25View::rerun_button_pressed () +{ + // NOTE: we use deferred execution, because otherwise the button won't get repainted properly + dm_rerun_macro (); +} + +void +D25View::rerun_macro () +{ +BEGIN_PROTECTED + + if (! m_generator.empty ()) { + std::map add_pars; + tl::Recipe::make (m_generator, add_pars); + } + +END_PROTECTED +} + void D25View::fit_button_clicked () { diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.h b/src/plugins/tools/view_25d/lay_plugin/layD25View.h index 9976b15cf..5495925f9 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25View.h +++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.h @@ -64,6 +64,7 @@ public: static D25View *open (lay::LayoutView *view); void close (); void clear (); + void begin (const std::string &generator); void open_display (const color_t *frame_color, const color_t *fill_color, const db::LayerProperties *like); void close_display (); void entry (const db::Region &data, double dbu, double zstart, double zstop); @@ -82,12 +83,16 @@ private slots: void vscale_slider_changed (int value); void vscale_value_edited (); void init_failed (); + void rerun_button_pressed (); private: Ui::D25View *mp_ui; + tl::DeferredMethod dm_rerun_macro; + std::string m_generator; void cellviews_changed (); void layer_properties_changed (int); + void rerun_macro (); }; } diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index 5150dc008..0772ffa52 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -205,6 +205,7 @@ D25ViewWidget::D25ViewWidget (QWidget *parent) m_zset = false; m_display_open = false; mp_view = 0; + m_has_error = false; reset_viewport (); } @@ -616,7 +617,6 @@ D25ViewWidget::entry (const db::Region &data, double dbu, double zstart, double // sequential assignment lay::color_t color = mp_view->get_palette ().luminous_color_by_index (m_layers.size ()); - color_to_gl (color, m_layers.back ().frame_color); color_to_gl (color, m_layers.back ().fill_color); } @@ -639,190 +639,6 @@ D25ViewWidget::attach_view (LayoutView *view) mp_view = view; } -#if 0 // @@@ -namespace { - - class ZDataCache - { - public: - ZDataCache () { } - - std::vector operator() (lay::LayoutView *view, int cv_index, int layer_index) - { - std::map > >::const_iterator c = m_cache.find (cv_index); - if (c != m_cache.end ()) { - std::map >::const_iterator l = c->second.find (layer_index); - if (l != c->second.end ()) { - return l->second; - } else { - return std::vector (); - } - } - - std::map > &lcache = m_cache [cv_index]; - - const db::D25TechnologyComponent *comp = 0; - - const lay::CellView &cv = view->cellview (cv_index); - if (cv.is_valid () && cv->technology ()) { - const db::Technology *tech = cv->technology (); - comp = dynamic_cast (tech->component_by_name ("d25")); - } - - if (comp) { - - std::multimap zi_by_lp; - - db::D25TechnologyComponent::layers_type layers = comp->compile_from_source (); - for (db::D25TechnologyComponent::layers_type::const_iterator i = layers.begin (); i != layers.end (); ++i) { - zi_by_lp.insert (std::make_pair (i->layer (), *i)); - } - - const db::Layout &ly = cv->layout (); - for (int l = 0; l < int (ly.layers ()); ++l) { - if (ly.is_valid_layer (l)) { - db::LayerProperties lp = ly.get_properties (l); - std::multimap::const_iterator z = zi_by_lp.find (lp); - if ((z == zi_by_lp.end () || ! z->first.log_equal (lp)) && ! lp.name.empty ()) { - // If possible, try by name only - lp = db::LayerProperties (lp.name); - z = zi_by_lp.find (lp); - } - while (z != zi_by_lp.end () && z->first.log_equal (lp)) { - lcache[l].push_back (z->second); - ++z; - } - } - } - - } - - std::map >::const_iterator l = lcache.find (layer_index); - if (l != lcache.end ()) { - return l->second; - } else { - return std::vector (); - } - } - - private: - std::map > > m_cache; - }; - -} - -bool -D25ViewWidget::prepare_view () -{ - m_layers.clear (); - m_layer_to_info.clear (); - m_vertex_chunks.clear (); - m_line_chunks.clear (); - - bool zset = false; - m_zmin = m_zmax = 0.0; - - if (! mp_view) { - m_bbox = db::DBox (-1.0, -1.0, 1.0, 1.0); - return false; - } - - m_bbox = mp_view->viewport ().box (); - - ZDataCache zdata; - - // collect and confine to cell bbox - db::DBox cell_bbox; - for (lay::LayerPropertiesConstIterator lp = mp_view->begin_layers (); ! lp.at_end (); ++lp) { - - std::vector zinfo; - if (! lp->has_children ()) { - zinfo = zdata (mp_view, lp->cellview_index (), lp->layer_index ()); - } - - for (std::vector::const_iterator zi = zinfo.begin (); zi != zinfo.end (); ++zi) { - const lay::CellView &cv = mp_view->cellview ((unsigned int) lp->cellview_index ()); - cell_bbox += db::CplxTrans (cv->layout ().dbu ()) * cv.cell ()->bbox ((unsigned int) lp->layer_index ()); - } - - } - - bool any = false; - - tl::AbsoluteProgress progress (tl::to_string (tr ("Rendering ..."))); - - for (lay::LayerPropertiesConstIterator lp = mp_view->begin_layers (); ! lp.at_end (); ++lp) { - - std::vector zinfo; - if (! lp->has_children ()) { - zinfo = zdata (mp_view, lp->cellview_index (), lp->layer_index ()); - } - - for (std::vector::const_iterator zi = zinfo.begin (); zi != zinfo.end (); ++zi) { - - any = true; - - double z0 = zi->zstart (); - double z1 = zi->zstop (); - - m_vertex_chunks.push_back (triangle_chunks_type ()); - m_line_chunks.push_back (line_chunks_type ()); - - LayerInfo info; - lp_to_info (*lp, info); - info.vertex_chunk = &m_vertex_chunks.back (); - info.line_chunk = &m_line_chunks.back (); - - m_layer_to_info.insert (std::make_pair (std::make_pair (lp->cellview_index (), lp->layer_index ()), m_layers.size ())); - m_layers.push_back (info); - - const lay::CellView &cv = mp_view->cellview ((unsigned int) lp->cellview_index ()); - - render_layout (progress, m_vertex_chunks.back (), m_line_chunks.back (), cv->layout (), *cv.cell (), db::CplxTrans (cv->layout ().dbu ()).inverted () * m_bbox, (unsigned int) lp->layer_index (), z0, z1); - - if (! zset) { - m_zmin = z0; - m_zmax = z1; - zset = true; - } else { - m_zmin = std::min (z0, m_zmin); - m_zmax = std::max (z1, m_zmax); - } - - } - - } - - return any; -} - -void -D25ViewWidget::refresh_view () -{ - if (! mp_view) { - return; - } - - for (lay::LayerPropertiesConstIterator lp = mp_view->begin_layers (); ! lp.at_end (); ++lp) { - - std::pair key = std::make_pair (lp->cellview_index (), lp->layer_index ()); - - std::multimap, size_t>::const_iterator l = m_layer_to_info.find (key); - while (l != m_layer_to_info.end () && l->first == key) { - if (l->second < m_layers.size ()) { - lp_to_info (*lp, m_layers [l->second]); - } - ++l; - } - - } - - refresh (); -} - -// ----------------------- -#endif - void D25ViewWidget::render_polygon (D25ViewWidget::triangle_chunks_type &chunks, D25ViewWidget::line_chunks_type &line_chunks, const db::Polygon &poly, double dbu, double zstart, double zstop) { @@ -991,22 +807,22 @@ D25ViewWidget::initializeGL () tl_assert (m_gridplane_program == 0); tl_assert (m_lines_program == 0); - bool error = false; + m_has_error = false; try { do_initialize_gl (); } catch (tl::Exception &ex) { m_error = ex.msg (); - error = true; + m_has_error = true; } catch (std::exception &ex) { m_error = ex.what (); - error = true; + m_has_error = true; } catch (...) { m_error = "(unspecific error)"; - error = true; + m_has_error = true; } - if (error) { + if (m_has_error) { delete m_shapes_program; m_shapes_program = 0; diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h index 5b7879517..217d231b7 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h @@ -142,6 +142,11 @@ public: return m_error; } + bool has_error () const + { + return m_has_error; + } + void clear (); void open_display (const color_t *frame_color, const color_t *fill_color, const db::LayerProperties *like); void close_display (); @@ -165,6 +170,7 @@ private: std::unique_ptr mp_mode; QOpenGLShaderProgram *m_shapes_program, *m_lines_program, *m_gridplane_program; std::string m_error; + bool m_has_error; double m_scale_factor; double m_vscale_factor; QVector3D m_displacement; diff --git a/src/plugins/tools/view_25d/lay_plugin/templates/d25.lym b/src/plugins/tools/view_25d/lay_plugin/templates/d25.lym index ac0468824..b3049e1c7 100644 --- a/src/plugins/tools/view_25d/lay_plugin/templates/d25.lym +++ b/src/plugins/tools/view_25d/lay_plugin/templates/d25.lym @@ -42,8 +42,8 @@ # Options are: # # display(..., color: 0xff0000) use bright red for the material color (RGB) -# display(..., frame: 0xff0000) use bright red for the frame color (combine with "color" for the fill) -# display(..., hollow: true) use hollow style (only frame is drawn) +# display(..., frame: 0xff0000) use bright red for the frame color (combine with "fill" for the fill color) +# display(..., fill: 0x00ff00) use bright green for the fill color along (combine with "frame" for the frame color) # display(..., like: "7/0") borrow style from layout view's style for layer "7/0" # From 4acd0aabc5d69cdb572fe8343ecdf2db17f1c3a5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 6 Mar 2022 10:07:23 +0100 Subject: [PATCH 13/24] Removed D25 tech component --- src/db/db/db.pro | 2 - src/db/db/dbD25TechnologyComponent.cc | 436 ------------------ src/db/db/dbD25TechnologyComponent.h | 106 ----- .../dbD25TechnologyComponentTests.cc | 102 ---- src/db/unit_tests/unit_tests.pro | 1 - .../laybasic/D25TechnologyComponentEditor.ui | 55 --- .../laybasic/layD25TechnologyComponent.cc | 115 ----- .../laybasic/layD25TechnologyComponent.h | 57 --- src/laybasic/laybasic/laybasic.pro | 5 +- .../view_25d/lay_plugin/layD25ViewWidget.cc | 1 - 10 files changed, 1 insertion(+), 879 deletions(-) delete mode 100644 src/db/db/dbD25TechnologyComponent.cc delete mode 100644 src/db/db/dbD25TechnologyComponent.h delete mode 100644 src/db/unit_tests/dbD25TechnologyComponentTests.cc delete mode 100644 src/laybasic/laybasic/D25TechnologyComponentEditor.ui delete mode 100644 src/laybasic/laybasic/layD25TechnologyComponent.cc delete mode 100644 src/laybasic/laybasic/layD25TechnologyComponent.h diff --git a/src/db/db/db.pro b/src/db/db/db.pro index df5648b48..35293190d 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -204,7 +204,6 @@ SOURCES = \ gsiDeclDbNetlistCrossReference.cc \ gsiDeclDbLayoutVsSchematic.cc \ dbNetlistObject.cc \ - dbD25TechnologyComponent.cc \ gsiDeclDbTexts.cc \ dbTexts.cc \ dbDeepTexts.cc \ @@ -381,7 +380,6 @@ HEADERS = \ dbLayoutVsSchematicFormatDefs.h \ dbLayoutVsSchematic.h \ dbNetlistObject.h \ - dbD25TechnologyComponent.h \ dbTexts.h \ dbDeepTexts.h \ dbAsIfFlatTexts.h \ diff --git a/src/db/db/dbD25TechnologyComponent.cc b/src/db/db/dbD25TechnologyComponent.cc deleted file mode 100644 index 9b421ed7e..000000000 --- a/src/db/db/dbD25TechnologyComponent.cc +++ /dev/null @@ -1,436 +0,0 @@ - -/* - - KLayout Layout Viewer - Copyright (C) 2006-2022 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 "dbD25TechnologyComponent.h" -#include "tlClassRegistry.h" -#include "tlString.h" - -namespace db -{ - -std::string d25_component_name () -{ - return std::string ("d25"); -} - -std::string d25_description () -{ - return tl::to_string (tr ("Z stack (2.5d)")); -} - -// ----------------------------------------------------------------------------------------- -// D25LayerInfo implementation - -D25LayerInfo::D25LayerInfo () - : m_layer (), m_zstart (0.0), m_zstop (0.0) -{ - // .. nothing yet .. -} - -D25LayerInfo::~D25LayerInfo () -{ - // .. nothing yet .. -} - -D25LayerInfo::D25LayerInfo (const D25LayerInfo &other) -{ - operator= (other); -} - -D25LayerInfo & -D25LayerInfo::operator= (const D25LayerInfo &other) -{ - if (this != &other) { - m_layer = other.m_layer; - m_zstart = other.m_zstart; - m_zstop = other.m_zstop; - } - return *this; -} - -bool -D25LayerInfo::operator== (const D25LayerInfo &other) const -{ - return fabs (m_zstart - other.m_zstart) < db::epsilon && fabs (m_zstop - other.m_zstop) < db::epsilon; -} - -void -D25LayerInfo::set_layer (const db::LayerProperties &l) -{ - m_layer = l; -} - -void -D25LayerInfo::set_layer_from_string (const std::string &l) -{ - db::LayerProperties lp; - tl::Extractor ex (l.c_str ()); - try { - lp.read (ex); - } catch (tl::Exception &) { - // ignore errors for now. - } - m_layer = lp; -} - -std::string -D25LayerInfo::layer_as_string () const -{ - return m_layer.to_string (); -} - -void -D25LayerInfo::set_zstart (double z0) -{ - m_zstart = z0; -} - -void -D25LayerInfo::set_zstop (double z1) -{ - m_zstop = z1; -} - -// ----------------------------------------------------------------------------------- -// D25TechnologyComponent implementation - -D25TechnologyComponent::D25TechnologyComponent () - : db::TechnologyComponent (d25_component_name (), d25_description ()) -{ - // provide some explanation for the initialization - m_src = - "# Provide z stack information here\n" - "#\n" - "# Each line is one layer. The specification consists of a layer specification, a colon and arguments.\n" - "# The arguments are named (like \"x=...\") or in serial. Parameters are separated by comma or blanks.\n" - "# Named arguments are:\n" - "#\n" - "# zstart The lower z position of the extruded layer in µm\n" - "# zstop The upper z position of the extruded layer in µm\n" - "# height The height of the extruded layer in µm\n" - "#\n" - "# 'height', 'zstart' and 'zstop' can be used in any combination. If no value is given for 'zstart',\n" - "# the upper level of the previous layer will be used.\n" - "#\n" - "# If a single unnamed parameter is given, it corresponds to 'height'. Two parameters correspond to\n" - "# 'zstart' and 'zstop'.\n" - "#\n" - "# Examples:\n" - "# 1: 0.5 1.5 # extrude layer 1/0 from 0.5 to 1.5 vertically\n" - "# 1/0: 0.5 1.5 # same with explicit datatype\n" - "# 1: zstop=1.5, zstart=0.5 # same with named parameters\n" - "# 1: height=1.0, zstop=1.5 # same with z stop minus height\n" - "# 1: 1.0 zstop=1.5 # same with height as unnamed parameter\n" - "#\n" - "# VARIABLES\n" - "#\n" - "# You can declare variables with:\n" - "# var name = value\n" - "#\n" - "# You can use variables inside numeric expressions.\n" - "# Example:\n" - "# var hmetal = 0.48\n" - "# 7/0: 0.5 0.5+hmetal*2 # 2x thick metal\n" - "#\n" - "# You cannot use variables inside layer specifications currently.\n" - "#\n" - "# CONDITIONALS\n" - "#\n" - "# You can enable or disable branches of the table using 'if', 'else', 'elseif' and 'end':\n" - "# Example:\n" - "# var thick_m1 = true\n" - "# if thickm1\n" - "# 1: 0.5 1.5\n" - "# else\n" - "# 1: 0.5 1.2\n" - "# end\n" - "\n" - ; -} - -D25TechnologyComponent::D25TechnologyComponent (const D25TechnologyComponent &d) - : db::TechnologyComponent (d25_component_name (), d25_description ()) -{ - m_src = d.m_src; -} - -D25TechnologyComponent::layers_type -D25TechnologyComponent::compile_from_source () const -{ - layers_type layers; - - int current_line = 0; - - try { - - tl::Eval eval; - std::vector conditional_stack; - - std::vector lines = tl::split (m_src, "\n"); - for (std::vector::const_iterator l = lines.begin (); l != lines.end (); ++l) { - - ++current_line; - - tl::Extractor ex (l->c_str ()); - - if (ex.test ("#")) { - // ignore comments - } else if (ex.at_end ()) { - // ignore empty lines - - } else if (ex.test ("if")) { - - tl::Expression x; - eval.parse (x, ex); - conditional_stack.push_back (x.execute ().to_bool ()); - - ex.expect_end (); - - } else if (ex.test ("else")) { - - if (conditional_stack.empty ()) { - throw tl::Exception (tl::to_string (tr ("'else' without 'if'"))); - } - - conditional_stack.back () = ! conditional_stack.back (); - - ex.expect_end (); - - } else if (ex.test ("end")) { - - if (conditional_stack.empty ()) { - throw tl::Exception (tl::to_string (tr ("'end' without 'if'"))); - } - - conditional_stack.pop_back (); - - ex.expect_end (); - - } else if (ex.test ("elsif")) { - - if (conditional_stack.empty ()) { - throw tl::Exception (tl::to_string (tr ("'elsif' without 'if'"))); - } - - tl::Expression x; - eval.parse (x, ex); - conditional_stack.back () = x.execute ().to_bool (); - - ex.expect_end (); - - } else if (! conditional_stack.empty () && ! conditional_stack.back ()) { - - continue; - - } else if (ex.test ("var")) { - - std::string n; - ex.read_name (n); - - ex.expect ("="); - - tl::Expression x; - eval.parse (x, ex); - eval.set_var (n, x.execute ()); - - ex.expect_end (); - - } else if (ex.test ("print")) { - - tl::Expression x; - eval.parse (x, ex); - ex.expect_end (); - - tl::info << x.execute ().to_string (); - - } else if (ex.test ("error")) { - - tl::Expression x; - eval.parse (x, ex); - ex.expect_end (); - - throw tl::Exception (x.execute ().to_string ()); - - } else { - - db::D25LayerInfo info; - if (! layers.empty ()) { - info.set_zstart (layers.back ().zstop ()); - info.set_zstop (layers.back ().zstop ()); - } - - tl::Variant z0, z1, h; - std::vector args; - - db::LayerProperties lp; - lp.read (ex); - info.set_layer (lp); - - ex.expect (":"); - - while (! ex.at_end ()) { - - if (ex.test ("#")) { - break; - } - - tl::Expression pvx; - - std::string pn; - if (ex.try_read_name (pn)) { - ex.expect ("="); - eval.parse (pvx, ex); - } else { - eval.parse (pvx, ex); - } - - double pv = pvx.execute ().to_double (); - - ex.test (","); - - if (pn.empty ()) { - args.push_back (pv); - } else if (pn == "zstart") { - z0 = pv; - } else if (pn == "zstop") { - z1 = pv; - } else if (pn == "height") { - h = pv; - } else { - throw tl::Exception (tl::to_string (tr ("Invalid parameter name: ")) + pn); - } - - } - - if (args.size () == 0) { - if (z0.is_nil () && z1.is_nil ()) { - if (! h.is_nil ()) { - info.set_zstop (info.zstop () + h.to_double ()); - } - } else if (z0.is_nil ()) { - info.set_zstop (z1.to_double ()); - if (! h.is_nil ()) { - info.set_zstart (info.zstop () - h.to_double ()); - } - } else if (z1.is_nil ()) { - info.set_zstart (z0.to_double ()); - if (! h.is_nil ()) { - info.set_zstop (info.zstart () + h.to_double ()); - } - } else { - info.set_zstart (z0.to_double ()); - info.set_zstop (z1.to_double ()); - } - } else if (args.size () == 1) { - if (! h.is_nil ()) { - if (! z0.is_nil ()) { - throw tl::Exception (tl::to_string (tr ("Redundant parameters: zstart already given"))); - } - if (! z1.is_nil ()) { - throw tl::Exception (tl::to_string (tr ("Redundant parameters: zstop implicitly given"))); - } - info.set_zstart (args[0]); - info.set_zstop (args[0] + h.to_double ()); - } else { - if (! z1.is_nil ()) { - throw tl::Exception (tl::to_string (tr ("Redundant parameters: zstop implicitly given"))); - } - info.set_zstop ((! z0.is_nil () ? z0.to_double () : info.zstart ()) + args[0]); - } - } else if (args.size () == 2) { - if (! z0.is_nil ()) { - throw tl::Exception (tl::to_string (tr ("Redundant parameters: zstart already given"))); - } - if (! z1.is_nil ()) { - throw tl::Exception (tl::to_string (tr ("Redundant parameters: zstop already given"))); - } - if (! h.is_nil ()) { - throw tl::Exception (tl::to_string (tr ("Redundant parameters: height implicitly given"))); - } - info.set_zstart (args[0]); - info.set_zstop (args[1]); - } else { - throw tl::Exception (tl::to_string (tr ("Too many parameters (max 2)"))); - } - - layers.push_back (info); - - } - - } - - if (! conditional_stack.empty ()) { - throw tl::Exception (tl::to_string (tr ("'if', 'else' or 'elsif' without matching 'end'"))); - } - - } catch (tl::Exception &ex) { - throw tl::Exception (ex.msg () + tl::sprintf (tl::to_string (tr (" in line %d")), current_line)); - } - - return layers; -} - -std::string -D25TechnologyComponent::to_string () const -{ - layers_type layers = compile_from_source (); - std::string res; - - for (layers_type::const_iterator i = layers.begin (); i != layers.end (); ++i) { - if (! res.empty ()) { - res += "\n"; - } - res += i->layer ().to_string () + ": zstart=" + tl::to_string (i->zstart ()) + ", zstop=" + tl::to_string (i->zstop ()); - } - - return res; -} - -// ----------------------------------------------------------------------------------- -// D25TechnologyComponent technology component registration - -class D25TechnologyComponentProvider - : public db::TechnologyComponentProvider -{ -public: - D25TechnologyComponentProvider () - : db::TechnologyComponentProvider () - { - // .. nothing yet .. - } - - virtual db::TechnologyComponent *create_component () const - { - return new D25TechnologyComponent (); - } - - virtual tl::XMLElementBase *xml_element () const - { - return new db::TechnologyComponentXMLElement (d25_component_name (), - tl::make_member (&D25TechnologyComponent::src, &D25TechnologyComponent::set_src, "src") - ); - } -}; - -static tl::RegisteredClass tc_decl (new D25TechnologyComponentProvider (), 3100, d25_component_name ().c_str ()); - -} diff --git a/src/db/db/dbD25TechnologyComponent.h b/src/db/db/dbD25TechnologyComponent.h deleted file mode 100644 index db38b3124..000000000 --- a/src/db/db/dbD25TechnologyComponent.h +++ /dev/null @@ -1,106 +0,0 @@ - -/* - - KLayout Layout Viewer - Copyright (C) 2006-2022 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 - -*/ - -#ifndef HDR_dbD25TechnologyComponent -#define HDR_dbD25TechnologyComponent - -#include "dbTechnology.h" -#include "dbLayerProperties.h" - -namespace db -{ - -class DB_PUBLIC D25LayerInfo -{ -public: - D25LayerInfo (); - ~D25LayerInfo (); - D25LayerInfo (const D25LayerInfo &other); - D25LayerInfo &operator= (const D25LayerInfo &other); - - bool operator== (const D25LayerInfo &other) const; - - const db::LayerProperties &layer () const - { - return m_layer; - } - - void set_layer_from_string (const std::string &l); - std::string layer_as_string () const; - - void set_layer (const db::LayerProperties &l); - - double zstart () const - { - return m_zstart; - } - - void set_zstart (double z0); - - double zstop () const - { - return m_zstop; - } - - void set_zstop (double z1); - -private: - db::LayerProperties m_layer; - double m_zstart, m_zstop; -}; - -class DB_PUBLIC D25TechnologyComponent - : public db::TechnologyComponent -{ -public: - D25TechnologyComponent (); - D25TechnologyComponent (const D25TechnologyComponent &d); - - typedef std::list layers_type; - - layers_type compile_from_source () const; - - const std::string &src () const - { - return m_src; - } - - // for persistency only, use "compile_from_source" to read from a source string - void set_src (const std::string &s) - { - m_src = s; - } - - std::string to_string () const; - - db::TechnologyComponent *clone () const - { - return new D25TechnologyComponent (*this); - } - -private: - std::string m_src; -}; - -} - -#endif diff --git a/src/db/unit_tests/dbD25TechnologyComponentTests.cc b/src/db/unit_tests/dbD25TechnologyComponentTests.cc deleted file mode 100644 index 30ac2b807..000000000 --- a/src/db/unit_tests/dbD25TechnologyComponentTests.cc +++ /dev/null @@ -1,102 +0,0 @@ - -/* - - KLayout Layout Viewer - Copyright (C) 2006-2022 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 "dbD25TechnologyComponent.h" -#include "tlUnitTest.h" - - -TEST(1) -{ - db::D25TechnologyComponent comp; - - comp.set_src ("1/0: 1.0 1.5 # a comment"); - comp.compile_from_source (); - EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5"); - - comp.set_src ("1/0: zstart=1.0 zstop=1.5"); - comp.compile_from_source (); - EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5"); - - comp.set_src ("1/0: zstart=1.0 height=0.5"); - comp.compile_from_source (); - EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5"); - - comp.set_src ("1/0: 1.0 height=0.5"); - comp.compile_from_source (); - EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5"); - - comp.set_src ("1/0: zstop=1.5 height=0.5"); - comp.compile_from_source (); - EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5"); - - comp.set_src ("1/0: zstart=1.0 zstop=1.5\nname: height=3"); - comp.compile_from_source (); - EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5\nname: zstart=1.5, zstop=4.5"); - - comp.set_src ("1/0: zstart=1.0 zstop=1.5\nname: zstart=4.0 height=3\n\n# a comment line"); - comp.compile_from_source (); - EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5\nname: zstart=4, zstop=7"); - - comp.set_src ("var x=1.0\n1/0: zstart=x zstop=x+0.5\nname: zstart=4.0 height=3\n\n# a comment line"); - comp.compile_from_source (); - EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5\nname: zstart=4, zstop=7"); - - comp.set_src ("var x=1.0\nif x == 1.0\n1/0: zstart=x zstop=x+0.5\nelse\n1/0: zstart=0 zstop=0\nend\nname: zstart=4.0 height=3\n\n# a comment line"); - comp.compile_from_source (); - EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5\nname: zstart=4, zstop=7"); - - comp.set_src ("var x=2.0\nif x == 1.0\n1/0: zstart=x zstop=x+0.5\nelse\n1/0: zstart=0 zstop=0\nend\nname: zstart=4.0 height=3\n\n# a comment line"); - comp.compile_from_source (); - EXPECT_EQ (comp.to_string (), "1/0: zstart=0, zstop=0\nname: zstart=4, zstop=7"); - - try { - comp.set_src ("blabla"); - comp.compile_from_source (); - EXPECT_EQ (false, true); - } catch (...) { } - - try { - comp.set_src ("1/0: 1 2 3"); - comp.compile_from_source (); - EXPECT_EQ (false, true); - } catch (...) { } - - try { - comp.set_src ("1/0: foo=1 bar=2"); - comp.compile_from_source (); - EXPECT_EQ (false, true); - } catch (...) { } - - try { - comp.set_src ("1/0: 1;*2"); - comp.compile_from_source (); - EXPECT_EQ (false, true); - } catch (...) { } - - try { - comp.set_src ("error 42"); - comp.compile_from_source (); - EXPECT_EQ (false, true); - } catch (...) { } -} diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 8601dfb65..ffe564cb9 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -39,7 +39,6 @@ SOURCES = \ dbPolygonToolsTests.cc \ dbTechnologyTests.cc \ dbStreamLayerTests.cc \ - dbD25TechnologyComponentTests.cc \ dbVectorTests.cc \ dbVariableWidthPathTests.cc \ dbTransTests.cc \ diff --git a/src/laybasic/laybasic/D25TechnologyComponentEditor.ui b/src/laybasic/laybasic/D25TechnologyComponentEditor.ui deleted file mode 100644 index 19c8bfc39..000000000 --- a/src/laybasic/laybasic/D25TechnologyComponentEditor.ui +++ /dev/null @@ -1,55 +0,0 @@ - - - D25TechnologyComponentEditor - - - - 0 - 0 - 549 - 434 - - - - Settings - - - - - - <html>2.5d Vertical stack information (see <a href="int:/about/25d_view.xml">here</a> for details)</html> - - - - - - - - 0 - 0 - - - - Line - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - QTextEdit::NoWrap - - - false - - - - - - - - - diff --git a/src/laybasic/laybasic/layD25TechnologyComponent.cc b/src/laybasic/laybasic/layD25TechnologyComponent.cc deleted file mode 100644 index af6bbee16..000000000 --- a/src/laybasic/laybasic/layD25TechnologyComponent.cc +++ /dev/null @@ -1,115 +0,0 @@ - -/* - - KLayout Layout Viewer - Copyright (C) 2006-2022 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 "laybasicConfig.h" -#include "dbD25TechnologyComponent.h" -#include "layD25TechnologyComponent.h" -#include "layQtTools.h" - -#include -#include - -namespace lay -{ - -D25TechnologyComponentEditor::D25TechnologyComponentEditor (QWidget *parent) - : TechnologyComponentEditor (parent) -{ - setupUi (this); - - src_te->setFont (monospace_font ()); - - activate_help_links (label); - - QResource res (tl::to_qstring (":/syntax/d25_text.xml")); - QByteArray data ((const char *) res.data (), int (res.size ())); -#if QT_VERSION >= 0x60000 - if (res.compressionAlgorithm () == QResource::ZlibCompression) { -#else - if (res.isCompressed ()) { -#endif - data = qUncompress (data); - } - - QBuffer input (&data); - input.open (QIODevice::ReadOnly); - mp_hl_basic_attributes.reset (new GenericSyntaxHighlighterAttributes ()); - mp_hl_attributes.reset (new GenericSyntaxHighlighterAttributes (mp_hl_basic_attributes.get ())); - lay::GenericSyntaxHighlighter *hl = new GenericSyntaxHighlighter (src_te, input, mp_hl_attributes.get ()); - input.close (); - - hl->setDocument (src_te->document ()); - - connect (src_te, SIGNAL (cursorPositionChanged ()), this, SLOT (cursor_position_changed ())); -} - -void -D25TechnologyComponentEditor::cursor_position_changed () -{ - int line = src_te->textCursor ().block ().firstLineNumber () + 1; - lnum_label->setText (tl::to_qstring (tl::sprintf (tl::to_string (tr ("Line %d")), line))); -} - -void -D25TechnologyComponentEditor::commit () -{ - db::D25TechnologyComponent *data = dynamic_cast (tech_component ()); - if (! data) { - return; - } - - std::string src = tl::to_string (src_te->toPlainText ()); - - // test-compile before setting it - db::D25TechnologyComponent tc; - tc.set_src (src); - tc.compile_from_source (); - - data->set_src (src); -} - -void -D25TechnologyComponentEditor::setup () -{ - db::D25TechnologyComponent *data = dynamic_cast (tech_component ()); - if (! data) { - return; - } - - src_te->setPlainText (tl::to_qstring (data->src ())); -} - -class D25TechnologyComponentEditorProvider - : public lay::TechnologyEditorProvider -{ -public: - virtual lay::TechnologyComponentEditor *create_editor (QWidget *parent) const - { - return new D25TechnologyComponentEditor (parent); - } -}; - -static tl::RegisteredClass editor_decl (new D25TechnologyComponentEditorProvider (), 3100, "d25"); - -} // namespace lay - diff --git a/src/laybasic/laybasic/layD25TechnologyComponent.h b/src/laybasic/laybasic/layD25TechnologyComponent.h deleted file mode 100644 index 5cdf03df8..000000000 --- a/src/laybasic/laybasic/layD25TechnologyComponent.h +++ /dev/null @@ -1,57 +0,0 @@ - -/* - - KLayout Layout Viewer - Copyright (C) 2006-2022 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 - -*/ - - -#ifndef HDR_layD25TechnologyComponent -#define HDR_layD25TechnologyComponent - -#include "ui_D25TechnologyComponentEditor.h" -#include "layTechnology.h" -#include "layGenericSyntaxHighlighter.h" - -#include - -namespace lay { - -class D25TechnologyComponentEditor - : public lay::TechnologyComponentEditor, - public Ui::D25TechnologyComponentEditor -{ -Q_OBJECT - -public: - D25TechnologyComponentEditor (QWidget *parent); - - void commit (); - void setup (); - -private slots: - void cursor_position_changed (); - -private: - std::unique_ptr mp_hl_attributes, mp_hl_basic_attributes; -}; - -} - -#endif - diff --git a/src/laybasic/laybasic/laybasic.pro b/src/laybasic/laybasic/laybasic.pro index a9cd72e78..ed2625d9d 100644 --- a/src/laybasic/laybasic/laybasic.pro +++ b/src/laybasic/laybasic/laybasic.pro @@ -74,8 +74,7 @@ FORMS = \ NetInfoDialog.ui \ NetExportDialog.ui \ SelectCellViewForm.ui \ - LayoutStatistics.ui \ - D25TechnologyComponentEditor.ui + LayoutStatistics.ui RESOURCES = \ laybasicResources.qrc \ @@ -192,7 +191,6 @@ SOURCES = \ laySelectCellViewForm.cc \ layLayoutStatisticsForm.cc \ gsiDeclLayNetlistBrowserDialog.cc \ - layD25TechnologyComponent.cc \ layLayoutViewFunctions.cc HEADERS = \ @@ -300,7 +298,6 @@ HEADERS = \ layDispatcher.h \ laySelectCellViewForm.h \ layLayoutStatisticsForm.h \ - layD25TechnologyComponent.h \ layLayoutViewFunctions.h INCLUDEPATH += $$TL_INC $$GSI_INC $$DB_INC $$RDB_INC $$LYM_INC diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index 0772ffa52..523788074 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -26,7 +26,6 @@ #include "layLayoutView.h" #include "dbRecursiveShapeIterator.h" -#include "dbD25TechnologyComponent.h" #include "dbEdgeProcessor.h" #include "dbPolygonGenerators.h" #include "dbPolygonTools.h" From f681d96558d9b88b4bb24534e32d14d126d42a7f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 6 Mar 2022 10:35:58 +0100 Subject: [PATCH 14/24] WIP: Streamlined d25 script notation --- .../lay_plugin/built-in-macros/_d25_engine.rb | 100 +++++++++++------- .../view_25d/lay_plugin/templates/d25.lym | 34 +++--- 2 files changed, 78 insertions(+), 56 deletions(-) diff --git a/src/plugins/tools/view_25d/lay_plugin/built-in-macros/_d25_engine.rb b/src/plugins/tools/view_25d/lay_plugin/built-in-macros/_d25_engine.rb index e4a0e6376..534d72650 100644 --- a/src/plugins/tools/view_25d/lay_plugin/built-in-macros/_d25_engine.rb +++ b/src/plugins/tools/view_25d/lay_plugin/built-in-macros/_d25_engine.rb @@ -20,12 +20,47 @@ module D25 class D25Display - attr_accessor :fill, :frame, :like + attr_accessor :fill, :frame, :like, :name def initialize self.fill = nil self.frame = nil self.like = nil + self.name = nil + end + + def set_fill(arg) + if !arg.is_a?(0xffffff.class) + raise("'fill' must be a color value (an integer)") + end + self.fill = arg + end + + def set_frame(arg) + if !arg.is_a?(0xffffff.class) + raise("'frame' must be a color value (an integer)") + end + self.frame = arg + end + + def set_color(arg) + if !arg.is_a?(0xffffff.class) + raise("'color' must be a color value (an integer)") + end + self.fill = arg + self.frame = nil + end + + def set_like(arg) + li = nil + if arg.is_a?(String) + li = RBA::LayerInfo::from_string(arg) + elsif arg.is_a?(RBA::LayerInfo) + li = arg + else + raise("'like' must be a string or LayerInfo object") + end + self.like = li end end @@ -58,6 +93,7 @@ module D25 zstart = nil zstop = nil height = nil + display = D25Display::new args.each do |a| @@ -88,19 +124,31 @@ module D25 end height = a[:height] end + if a[:zstart] if zstart raise("Duplicate zstart specification") end zstart = a[:zstart] end + if a[:zstop] if zstop raise("Duplicate zstop specification") end zstop = a[:zstop] end - invalid_keys = a.keys.select { |k| ![ :height, :zstart, :zstop ].member?(k) } + + a[:color] && display.set_color(a[:color]) + a[:frame] && display.set_frame(a[:frame]) + a[:fill] && display.set_fill(a[:fill]) + a[:like] && display.set_like(a[:like]) + + if a[:name] + display.name = a[:name].to_s + end + + invalid_keys = a.keys.select { |k| ![ :height, :zstart, :zstop, :color, :frame, :fill, :like, :name ].member?(k) } if invalid_keys.size > 0 raise("Keyword argument(s) not understood: #{invalid_keys.collect(&:to_s).join(',')}") end @@ -128,7 +176,7 @@ module D25 raise("No layer specified") end - info = D25ZInfo::new(layer, zstart, zstop, @display || D25Display::new) + info = D25ZInfo::new(layer, zstart, zstop, @display || display) @zstack << info return info @@ -137,7 +185,7 @@ module D25 end - def display(*args, &block) + def zz(*args, &block) begin @@ -156,46 +204,16 @@ module D25 elsif a.is_a?(Hash) - hollow_fill = 0xffffffff + a[:color] && display.set_color(a[:color]) + a[:frame] && display.set_frame(a[:frame]) + a[:fill] && display.set_fill(a[:fill]) + a[:like] && display.set_like(a[:like]) - if a[:color] - if !a[:color].is_a?(0xffffff.class) - raise("'color' must be a color value (an integer)") - end - display.fill = a[:color] - display.frame = nil - end - if a[:frame] - if !a[:frame].is_a?(0xffffff.class) - raise("'frame' must be a color value (an integer)") - end - display.frame = a[:frame] - end - if a[:fill] - if !a[:fill].is_a?(0xffffff.class) - raise("'fill' must be a color value (an integer)") - end - display.fill = a[:fill] - end - if a[:hollow] - if a[:hollow] - display.fill = hollow_fill - end + if a[:name] + display.name = a[:name].to_s end - if a[:like] - li = nil - if a[:like].is_a?(String) - li = RBA::LayerInfo::from_string(a[:like]) - elsif a[:like].is_a?(RBA::LayerInfo) - li = a[:like] - else - raise("'like' must be a string or LayerInfo object") - end - display.like = li - end - - invalid_keys = a.keys.select { |k| ![ :fill, :frame, :color, :hollow, :like ].member?(k) } + invalid_keys = a.keys.select { |k| ![ :fill, :frame, :color, :hollow, :like, :name ].member?(k) } if invalid_keys.size > 0 raise("Keyword argument(s) not understood: #{invalid_keys.collect(&:to_s).join(',')}") end diff --git a/src/plugins/tools/view_25d/lay_plugin/templates/d25.lym b/src/plugins/tools/view_25d/lay_plugin/templates/d25.lym index b3049e1c7..7580c81c6 100644 --- a/src/plugins/tools/view_25d/lay_plugin/templates/d25.lym +++ b/src/plugins/tools/view_25d/lay_plugin/templates/d25.lym @@ -22,34 +22,38 @@ # z(layer, options ...): # # This function generates a extruded view from the given layer. -# The "options" describe the z extension. Valid forms are: +# +# Some options control the z extrusion: # # z(layer, 0.1 .. 0.2) extrude layer to z = 0.1 to 0.2 um # z(layer, zstart: 0.1, zstop: 0.2) same as above # z(layer, zstart: 0.1, height: 0.1) same as above, but with height instead of zstop # z(layer, height: 200.nm) extrude layer from last z position with a height of 200nm +# +# You can specify display options: +# +# z(..., color: 0xff0000) use bright red for the material color (RGB) +# z(..., frame: 0xff0000) use bright red for the frame color (combine with "fill" for the fill color) +# z(..., fill: 0x00ff00) use bright green for the fill color along (combine with "frame" for the frame color) +# z(..., like: "7/0") borrow style from layout view's style for layer "7/0" +# z(..., name: "M1") assigns a name to show for the material # -# If layer is an original layer, the display options (colors, hollow) are taken +# If no display options are given and layer is an original layer, the colors are taken # from the original layer's display style. Otherwise KLayout decides about the -# initial display options. Use "display(...)" to control the display options. +# colors itself using the application's palette. # -# display(options) { block } -# display(z(...), z(...), ..., options): +# zz(options) { block } +# zz(z(...), z(...), ..., options): # -# Specify the display options to use for the layers generated inside the block -# (which must contains at least one "z" call) or the given z calls: -# -# Options are: -# -# display(..., color: 0xff0000) use bright red for the material color (RGB) -# display(..., frame: 0xff0000) use bright red for the frame color (combine with "fill" for the fill color) -# display(..., fill: 0x00ff00) use bright green for the fill color along (combine with "frame" for the frame color) -# display(..., like: "7/0") borrow style from layout view's style for layer "7/0" +# Creates a display group. The display options are the same for all +# extrusion specs within the group. # +# The options of "zz" are "name", "color", "frame", "fill" and "like". z(input(1, 0), 0.1 .. 0.2) +z(input(2, 0), height: 250.nm, color: 0xffbc80) -display(like: 7/0) do +zz(like: 7/0, name: "Metal") do z(input(7, 0), height: 100.nm) z(input(8, 0), height: 150.nm) end From 33cd9fe68781b70dd821ba62912f114f55df3aee Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 6 Mar 2022 18:48:23 +0100 Subject: [PATCH 15/24] WIP: bug fixes --- src/plugins/tools/view_25d/lay_plugin/layD25View.cc | 12 ++++++++++-- src/plugins/tools/view_25d/lay_plugin/layD25View.h | 2 ++ .../tools/view_25d/lay_plugin/layD25ViewWidget.cc | 9 +++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc index 7e9a79b1a..dbd204051 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc @@ -38,7 +38,8 @@ const double initial_elevation = 15.0; D25View::D25View (lay::Dispatcher *root, lay::LayoutView *view) : lay::Browser (root, view, "d25_view"), - dm_rerun_macro (this, &D25View::rerun_macro) + dm_rerun_macro (this, &D25View::rerun_macro), + dm_fit (this, &D25View::fit) { mp_ui = new Ui::D25View (); mp_ui->setupUi (this); @@ -207,13 +208,20 @@ D25View::finish () mp_ui->d25_view->reset (); mp_ui->d25_view->set_cam_azimuth (0.0); mp_ui->d25_view->set_cam_elevation (-initial_elevation); - mp_ui->d25_view->fit (); + // NOTE: needs to be delayed to allow the geometry to be updated before (initial call) + dm_fit (); mp_ui->gl_stack->setCurrentIndex (0); } } +void +D25View::fit () +{ + mp_ui->d25_view->fit (); +} + static QString scale_factor_to_string (double f) { return QString (QString::fromUtf8 ("%1")).arg (f, 0, 'g', 3); diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.h b/src/plugins/tools/view_25d/lay_plugin/layD25View.h index 5495925f9..9979114f0 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25View.h +++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.h @@ -88,11 +88,13 @@ private slots: private: Ui::D25View *mp_ui; tl::DeferredMethod dm_rerun_macro; + tl::DeferredMethod dm_fit; std::string m_generator; void cellviews_changed (); void layer_properties_changed (int); void rerun_macro (); + void fit (); }; } diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index 523788074..b911edca5 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -592,6 +592,15 @@ D25ViewWidget::entry (const db::Region &data, double dbu, double zstart, double { tl_assert (m_display_open); + if (! m_zset) { + m_zmin = std::min (zstart, zstop); + m_zmax = std::max (zstart, zstop); + m_zset = true; + } else { + m_zmin = std::min (m_zmin, std::min (zstart, zstop)); + m_zmax = std::min (m_zmax, std::max (zstart, zstop)); + } + // try to establish a default color from the region's origin if required const db::OriginalLayerRegion *original_region = dynamic_cast (data.delegate ()); if (mp_view && m_layers.back ().fill_color [3] == 0.0 && m_layers.back ().frame_color [3] == 0.0) { From dda18e6f53188a08da39b9736f83e6df5921622c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 6 Mar 2022 19:01:16 +0100 Subject: [PATCH 16/24] WIP: Introducing names --- .../lay_plugin/built-in-macros/_d25_engine.rb | 2 +- .../view_25d/lay_plugin/gsiDeclLayD25View.cc | 2 +- .../tools/view_25d/lay_plugin/layD25View.cc | 4 ++-- .../tools/view_25d/lay_plugin/layD25View.h | 2 +- .../view_25d/lay_plugin/layD25ViewWidget.cc | 24 +++++++++++++++---- .../view_25d/lay_plugin/layD25ViewWidget.h | 4 +++- 6 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/plugins/tools/view_25d/lay_plugin/built-in-macros/_d25_engine.rb b/src/plugins/tools/view_25d/lay_plugin/built-in-macros/_d25_engine.rb index 534d72650..54949b9a3 100644 --- a/src/plugins/tools/view_25d/lay_plugin/built-in-macros/_d25_engine.rb +++ b/src/plugins/tools/view_25d/lay_plugin/built-in-macros/_d25_engine.rb @@ -260,7 +260,7 @@ module D25 displays.each do |k,zz| display = zz[0].display - view.open_display(display.frame, display.fill, display.like) + view.open_display(display.frame, display.fill, display.like, display.name) zz.each do |z| view.entry(z.layer.data, self.dbu, z.zstart, z.zstop) end diff --git a/src/plugins/tools/view_25d/lay_plugin/gsiDeclLayD25View.cc b/src/plugins/tools/view_25d/lay_plugin/gsiDeclLayD25View.cc index 0c771858a..4f614c1ce 100644 --- a/src/plugins/tools/view_25d/lay_plugin/gsiDeclLayD25View.cc +++ b/src/plugins/tools/view_25d/lay_plugin/gsiDeclLayD25View.cc @@ -65,7 +65,7 @@ Class decl_D25View (QT_EXTERNAL_BASE (QDialog) "lay", "D25View", gsi::method ("begin", &lay::D25View::begin, gsi::arg ("generator"), "@brief Initiates delivery of display groups" ) + - gsi::method ("open_display", &lay::D25View::open_display, gsi::arg ("frame_color"), gsi::arg ("fill_color"), gsi::arg ("like"), + gsi::method ("open_display", &lay::D25View::open_display, gsi::arg ("frame_color"), gsi::arg ("fill_color"), gsi::arg ("like"), gsi::arg ("name"), "@brief Creates a new display group" ) + gsi::method ("entry", &lay::D25View::entry, gsi::arg ("data"), gsi::arg ("dbu"), gsi::arg ("zstart"), gsi::arg ("zstop"), diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc index dbd204051..142c83c6c 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc @@ -173,10 +173,10 @@ D25View::begin (const std::string &generator) } void -D25View::open_display (const color_t *frame_color, const color_t *fill_color, const db::LayerProperties *like) +D25View::open_display (const color_t *frame_color, const color_t *fill_color, const db::LayerProperties *like, const std::string *name) { if (! mp_ui->d25_view->has_error ()) { - mp_ui->d25_view->open_display (frame_color, fill_color, like); + mp_ui->d25_view->open_display (frame_color, fill_color, like, name); } } diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.h b/src/plugins/tools/view_25d/lay_plugin/layD25View.h index 9979114f0..c891df965 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25View.h +++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.h @@ -65,7 +65,7 @@ public: void close (); void clear (); void begin (const std::string &generator); - void open_display (const color_t *frame_color, const color_t *fill_color, const db::LayerProperties *like); + void open_display (const color_t *frame_color, const color_t *fill_color, const db::LayerProperties *like, const std::string *name); void close_display (); void entry (const db::Region &data, double dbu, double zstart, double zstop); void finish (); diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index b911edca5..af8cbc9e5 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -555,7 +555,7 @@ static void lp_to_info (const lay::LayerPropertiesNode &lp, D25ViewWidget::Layer } void -D25ViewWidget::open_display (const color_t *frame_color, const color_t *fill_color, const db::LayerProperties *like) +D25ViewWidget::open_display (const color_t *frame_color, const color_t *fill_color, const db::LayerProperties *like, const std::string *name) { m_vertex_chunks.push_back (triangle_chunks_type ()); m_line_chunks.push_back (line_chunks_type ()); @@ -565,6 +565,14 @@ D25ViewWidget::open_display (const color_t *frame_color, const color_t *fill_col info.visible = true; color_to_gl (frame_color, info.frame_color); color_to_gl (fill_color, info.fill_color); + + info.has_name = (name != 0 || like != 0); + if (name) { + info.name = *name; + } else if (like) { + info.name = like->to_string (); + } + info.vertex_chunk = &m_vertex_chunks.back (); info.line_chunk = &m_line_chunks.back (); @@ -601,9 +609,11 @@ D25ViewWidget::entry (const db::Region &data, double dbu, double zstart, double m_zmax = std::min (m_zmax, std::max (zstart, zstop)); } + LayerInfo &info = m_layers.back (); + // try to establish a default color from the region's origin if required const db::OriginalLayerRegion *original_region = dynamic_cast (data.delegate ()); - if (mp_view && m_layers.back ().fill_color [3] == 0.0 && m_layers.back ().frame_color [3] == 0.0) { + if (mp_view && info.fill_color [3] == 0.0 && info.frame_color [3] == 0.0) { if (original_region) { @@ -614,7 +624,11 @@ D25ViewWidget::entry (const db::Region &data, double dbu, double zstart, double for (lay::LayerPropertiesConstIterator lp = mp_view->begin_layers (); ! lp.at_end (); ++lp) { if (! lp->has_children () && lp->source (true).layer_props ().log_equal (like)) { - lp_to_info (*lp, m_layers.back ()); + lp_to_info (*lp, info); + if (! info.has_name) { + info.name = like.to_string (); + info.has_name = true; + } break; } } @@ -625,7 +639,7 @@ D25ViewWidget::entry (const db::Region &data, double dbu, double zstart, double // sequential assignment lay::color_t color = mp_view->get_palette ().luminous_color_by_index (m_layers.size ()); - color_to_gl (color, m_layers.back ().fill_color); + color_to_gl (color, info.fill_color); } @@ -638,7 +652,7 @@ D25ViewWidget::entry (const db::Region &data, double dbu, double zstart, double void D25ViewWidget::finish () { - // @@@ + // .. nothing yet .. } void diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h index 217d231b7..7c091fe7b 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h @@ -89,6 +89,8 @@ public: GLfloat fill_color [4]; GLfloat frame_color [4]; bool visible; + std::string name; + bool has_name; }; D25ViewWidget (QWidget *parent); @@ -148,7 +150,7 @@ public: } void clear (); - void open_display (const color_t *frame_color, const color_t *fill_color, const db::LayerProperties *like); + void open_display (const color_t *frame_color, const color_t *fill_color, const db::LayerProperties *like, const std::string *name); void close_display (); void entry (const db::Region &data, double dbu, double zstart, double zstop); void finish (); From 1da12970ab5603ae9c650ee83a609c7dc7eb3473 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 6 Mar 2022 21:11:32 +0100 Subject: [PATCH 17/24] Material list, visibility --- .../tools/view_25d/lay_plugin/D25View.ui | 80 +++++++++++- .../tools/view_25d/lay_plugin/layD25View.cc | 115 +++++++++++++++++- .../tools/view_25d/lay_plugin/layD25View.h | 6 + .../view_25d/lay_plugin/layD25ViewWidget.cc | 11 +- .../view_25d/lay_plugin/layD25ViewWidget.h | 7 ++ 5 files changed, 215 insertions(+), 4 deletions(-) diff --git a/src/plugins/tools/view_25d/lay_plugin/D25View.ui b/src/plugins/tools/view_25d/lay_plugin/D25View.ui index d8e818856..e59d3d4d6 100644 --- a/src/plugins/tools/view_25d/lay_plugin/D25View.ui +++ b/src/plugins/tools/view_25d/lay_plugin/D25View.ui @@ -424,7 +424,23 @@ - + + + + 0 + 0 + + + + QAbstractItemView::ExtendedSelection + + + 0 + + + true + + @@ -566,6 +582,36 @@ + + + Select All + + + + + Unselect All + + + + + Show All + + + + + Hide All + + + + + Show Selected + + + + + Hide Selected + + @@ -594,5 +640,37 @@ + + select_all_action + triggered() + material_list + selectAll() + + + -1 + -1 + + + 716 + 336 + + + + + unselect_all_action + triggered() + material_list + clearSelection() + + + -1 + -1 + + + 716 + 336 + + + diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc index 142c83c6c..91fb79a5c 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc @@ -31,6 +31,8 @@ #include +#include + namespace lay { @@ -61,6 +63,10 @@ D25View::D25View (lay::Dispatcher *root, lay::LayoutView *view) connect (mp_ui->d25_view, SIGNAL (vscale_factor_changed(double)), this, SLOT (vscale_factor_changed(double))); connect (mp_ui->d25_view, SIGNAL (init_failed()), this, SLOT (init_failed())); connect (mp_ui->rerun_button, SIGNAL (clicked()), this, SLOT (rerun_button_pressed())); + connect (mp_ui->hide_all_action, SIGNAL (triggered()), this, SLOT (hide_all_triggered())); + connect (mp_ui->hide_selected_action, SIGNAL (triggered()), this, SLOT (hide_selected_triggered())); + connect (mp_ui->show_all_action, SIGNAL (triggered()), this, SLOT (show_all_triggered())); + connect (mp_ui->show_selected_action, SIGNAL (triggered()), this, SLOT (show_selected_triggered())); mp_ui->rerun_button->setEnabled (false); @@ -71,6 +77,25 @@ D25View::D25View (lay::Dispatcher *root, lay::LayoutView *view) view->cellviews_changed_event.add (this, &D25View::cellviews_changed); view->layer_list_changed_event.add (this, &D25View::layer_properties_changed); + + QPalette palette = mp_ui->material_list->palette (); + palette.setColor (QPalette::Base, Qt::black); + palette.setColor (QPalette::Text, Qt::white); + mp_ui->material_list->setPalette (palette); + + QFont font = mp_ui->material_list->font (); + font.setWeight (QFont::Bold); + mp_ui->material_list->setFont (font); + + mp_ui->material_list->addAction (mp_ui->select_all_action); + mp_ui->material_list->addAction (mp_ui->unselect_all_action); + mp_ui->material_list->addAction (mp_ui->show_all_action); + mp_ui->material_list->addAction (mp_ui->show_selected_action); + mp_ui->material_list->addAction (mp_ui->hide_all_action); + mp_ui->material_list->addAction (mp_ui->hide_selected_action); + mp_ui->material_list->setContextMenuPolicy (Qt::ActionsContextMenu); + + connect (mp_ui->material_list, SIGNAL (itemChanged (QListWidgetItem *)), this, SLOT (material_item_changed (QListWidgetItem *))); } D25View::~D25View () @@ -92,7 +117,7 @@ D25View::cellviews_changed () void D25View::layer_properties_changed (int) { - // @@@ mp_ui->d25_view->refresh_view (); + // .. nothing yet .. } void @@ -196,6 +221,35 @@ D25View::entry (const db::Region &data, double dbu, double zstart, double zstop) } } +static void layer_info_to_item (const lay::D25ViewWidget::LayerInfo &info, QListWidgetItem *item, size_t index, QSize icon_size) +{ + if (info.has_name) { + item->setText (tl::to_qstring (info.name)); + } else { + item->setText (tl::to_qstring ("#" + tl::to_string (index + 1))); + } + + QImage img (icon_size, QImage::Format_ARGB32); + img.fill (QColor (floor (info.fill_color [0] * 255 + 0.5), floor (info.fill_color [1] * 255 + 0.5), floor (info.fill_color [2] * 255 + 0.5), floor (info.fill_color [3] * 255 + 0.5))); + + QColor fc (floor (info.frame_color [0] * 255 + 0.5), floor (info.frame_color [1] * 255 + 0.5), floor (info.frame_color [2] * 255 + 0.5), floor (info.frame_color [3] * 255 + 0.5)); + if (fc.alpha () > 0) { + QRgb fc_rgb = fc.rgba (); + for (int x = 0; x < icon_size.width (); ++x) { + img.setPixel (x, 0, fc_rgb); + img.setPixel (x, icon_size.height () - 1, fc_rgb); + } + for (int y = 0; y < icon_size.height (); ++y) { + img.setPixel (0, y, fc_rgb); + img.setPixel (icon_size.width () - 1, y, fc_rgb); + } + } + + QIcon icon; + icon.addPixmap (QPixmap::fromImage (img)); + item->setIcon (icon); +} + void D25View::finish () { @@ -203,7 +257,19 @@ D25View::finish () mp_ui->d25_view->finish (); - // @@@ install layer properties widget + QFontMetrics fm (mp_ui->material_list->font ()); + QSize icon_size = fm.size (Qt::TextSingleLine, "WW"); + icon_size.setHeight (icon_size.height () - 2); + mp_ui->material_list->setIconSize (icon_size); + + mp_ui->material_list->clear (); + const std::vector &layers = mp_ui->d25_view->layers (); + for (auto l = layers.begin (); l != layers.end (); ++l) { + QListWidgetItem *item = new QListWidgetItem (mp_ui->material_list); + item->setFlags (item->flags () | Qt::ItemIsUserCheckable); + item->setCheckState (Qt::Checked); + layer_info_to_item (*l, item, l - layers.begin (), icon_size); + } mp_ui->d25_view->reset (); mp_ui->d25_view->set_cam_azimuth (0.0); @@ -298,6 +364,15 @@ D25View::vscale_factor_changed (double f) mp_ui->vzoom_slider->blockSignals (false); } +void +D25View::material_item_changed (QListWidgetItem *item) +{ + int index = mp_ui->material_list->row (item); + if (index >= 0) { + mp_ui->d25_view->set_material_visible (size_t (index), item->checkState () == Qt::Checked); + } +} + void D25View::deactivated () { @@ -366,6 +441,42 @@ D25View::fit_button_clicked () mp_ui->d25_view->fit (); } +void +D25View::hide_all_triggered () +{ + for (int i = 0; i < mp_ui->material_list->count (); ++i) { + mp_ui->material_list->item (i)->setCheckState (Qt::Unchecked); + } +} + +void +D25View::hide_selected_triggered () +{ + for (int i = 0; i < mp_ui->material_list->count (); ++i) { + if (mp_ui->material_list->item (i)->isSelected ()) { + mp_ui->material_list->item (i)->setCheckState (Qt::Unchecked); + } + } +} + +void +D25View::show_all_triggered () +{ + for (int i = 0; i < mp_ui->material_list->count (); ++i) { + mp_ui->material_list->item (i)->setCheckState (Qt::Checked); + } +} + +void +D25View::show_selected_triggered () +{ + for (int i = 0; i < mp_ui->material_list->count (); ++i) { + if (mp_ui->material_list->item (i)->isSelected ()) { + mp_ui->material_list->item (i)->setCheckState (Qt::Checked); + } + } +} + void D25View::accept () { diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.h b/src/plugins/tools/view_25d/lay_plugin/layD25View.h index c891df965..e28cd447a 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25View.h +++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.h @@ -24,6 +24,7 @@ #define HDR_layD25View #include +#include #include "tlObject.h" #include "layBrowser.h" @@ -84,6 +85,11 @@ private slots: void vscale_value_edited (); void init_failed (); void rerun_button_pressed (); + void material_item_changed (QListWidgetItem *); + void hide_all_triggered (); + void hide_selected_triggered (); + void show_all_triggered (); + void show_selected_triggered (); private: Ui::D25View *mp_ui; diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index af8cbc9e5..565f5721a 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -481,6 +481,15 @@ D25ViewWidget::refresh () update (); } +void +D25ViewWidget::set_material_visible (size_t index, bool visible) +{ + if (index < m_layers.size () && m_layers [index].visible != visible) { + m_layers [index].visible = visible; + update (); + } +} + void D25ViewWidget::showEvent (QShowEvent *) { @@ -551,7 +560,7 @@ static void lp_to_info (const lay::LayerPropertiesNode &lp, D25ViewWidget::Layer info.frame_color [3] = 0.0f; } - info.visible = lp.visible (true); + info.visible = true; } void diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h index 7c091fe7b..a553f8122 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h @@ -149,6 +149,13 @@ public: return m_has_error; } + const std::vector &layers () const + { + return m_layers; + } + + void set_material_visible (size_t index, bool visible); + void clear (); void open_display (const color_t *frame_color, const color_t *fill_color, const db::LayerProperties *like, const std::string *name); void close_display (); From 72233dc678b43e74b69de8af605d147d54f9b1cb Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 6 Mar 2022 23:05:45 +0100 Subject: [PATCH 18/24] Documentation --- src/lay/lay/doc/about/25d_screenshot.png | Bin 76723 -> 158000 bytes src/lay/lay/doc/about/25d_screenshot2.png | Bin 0 -> 74387 bytes src/lay/lay/doc/about/25d_view.xml | 216 ++++++++++++++++------ src/lay/lay/layHelpResources.qrc | 1 + 4 files changed, 158 insertions(+), 59 deletions(-) create mode 100644 src/lay/lay/doc/about/25d_screenshot2.png diff --git a/src/lay/lay/doc/about/25d_screenshot.png b/src/lay/lay/doc/about/25d_screenshot.png index 1796a0573cd6576ba1a109ee75708506cd7d72ed..32c96174e07671154fd31a467ec6c679fc982838 100644 GIT binary patch literal 158000 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
  • p!@J9xhhD7h#xIU&Pg3fljUHeyg0`J%(%XnfCR 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 PUZsEAM7vJ&&XfNK#(@#D literal 76723 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 z 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 Pz<

    - To open the view, use "Tools/2.5d View". Currently, the performance is limited, a rough number for a + Currently, the performance is limited, a rough number for a practical limit is around 100k polygons. The 2.5d view is only available, if KLayout was compiled with OpenGL support.

    +

    + In order to use the tool, you will need a script generating the material stack. + Such a script is a variant of a DRC script (see ). + The DRC language is used to import or generate polygon layers which are then + extruded and placed on a certain z level. +

    + +

    + To create a new script, use "Tools/2.5d View/New 2.5d Script". This will create a new script in the + macro editor. +

    + +

    + A simple script is this one. It takes two layers - 1/0 and 2/0 - and extrudes then in a + stacked fashion, the first with 200nm thickness and the second one with 300nm: +

    + +
    +z(input(1, 0), zstart: 0.1.um, height: 200.nm)  # extrudes layer 1/0 to a height of 200nm starting at z=100nm
    +z(input(2, 0), height: 300.nm)                  # adds layer 2/0 for the next 300nm
    +
    + +

    + To run the script, use the "Run" button from the macro IDE or pick the script + from the script list in the "Tools/2.5d View" menu. If your script is not shown in that + menu, check if it is configured to be bound to a menu item. +

    + +

    + After the script was executed, the 2.5d window is displayed. If you closed that window, you can + re-open it with "Tools/2.5d View/Open Window". The window will show the layout section visible + in the layout view. To refresh the scene - also after changing the script - either run the script + again from the macro IDE or use the green "re-run" button in the upper left corner of the 2.5d view + window. +

    +

    -

    Setup

    +

    2.5d Script Anatomy

    - The 2.5d view needs a technology setup explaining the way the layers are transformed into planes. - The setup is provided within a technology. Open the technology manager (File/Manage Technologies) and - navigate to the "Z Stack (2.5d)" component. The setup is basically a list of entries listing the - layer from which to take the shapes and the depth information. -

    - -

    - Each entry is a single line. Empty lines are ignored. Everything after a '#' character is - considered a comment. -

    - -

    - Each specification line consists of a layer specification, a colon and arguments. - The arguments are named (like "x=...") or in serial. Parameters are separated by comma or blanks. - Named arguments are: + As mentioned, a 2.5d script is a variant of a DRC script. You can basically use all features + of DRC, specifically boolean operations. Some practical restrictions exist:

      -
    • zstart: The lower z position of the extruded layer in µm
    • -
    • zstop: The upper z position of the extruded layer in µm
    • -
    • height: The height of the extruded layer in µm
    • +
    • You should not use external sources ("source" statement) as the 2.5d view is related to the loaded layout
    • +
    • Report generation or "output" statements are permitted, but do not make much sense in the context + of 2.5d view scripts.

    - 'height', 'zstart' and 'zstop' can be used in any combination. If no value is given for 'zstart', - the upper level of the previous layer will be used. + 2.5d scripts utilizes the DRC language with these two additional functions: +

    + +
      +
    • z(layer [, options])

      +

      Extrudes the given layer. "layer" is a (polygon) DRC layer. "options" declare the z extrusion and display parameters.

      +
    • +
    • zz( [options] ) { block }

      +

      Declares a material group which combines multiple "z" statements under a single display group. + This allows generating 3d material geometries which are more than a single extruded plane. + The display parameters then are specified within "zz" for all "z" calls inside the block.

      +
    • +
    + +

    "z" Function (plane extrusion)

    + + +

    + The layer argument of the function is a DRC layer which is rendered as an extruded sheet. + Further arguments control the height, z location and colors. + When used inside the "zz" block, the color options of the "z" calls are ignored and + taken from "zz" instead.

    - If a single unnamed parameter is given, it corresponds to 'height'. Two parameters correspond to - 'zstart' and 'zstop'. + Options for this function are:

    +
      +
    • zstart: specifies the bottom coordinate of the extruded sheet. If this option is not given, the top coordinate of the previous "z" statement is used.
    • +
    • zstop: specifies the top coordinate of the extruded sheet. Alternatively you can use "height".
    • +
    • height: specifies the extrusion height. Alternatively you can use "zstop".
    • +
    • color: specifies the color to use as a 24 bit hex RGB triplet (use "0xrrggbb" to specify the color similar to the HTML notation "#rrggbb"). + A color specification gives a single color with not differentiation of frame and wall colors.
    • +
    • frame: specifies the frame color to use as a 24 bit hex RGB triplet. If only a frame color is specified, the geometry will be rendered as wire frame only.
    • +
    • fill: specifies the fill (wall) color to use as a 24 bit hex RGB triplet. This allows specifying a different color for wall and frame when used with "frame".
    • +
    • like: specifies to use the same colors than used for some layer in the layout view. + If the layer is an original layer (i.e. taken from "input"), "like" defaults to the + original layer's source. If given, "like" needs to be a string representation of the + layer source (e.g. "7/0" for layer 7, datatype 0).
    • +
    • name: gives the material a name for displaying in the material list.
    • +
    +

    - Here are some examples: + Examples for the extrusion options:

    -
    1: 0.5 1.5                    # extrude layer 1/0 from 0.5 to 1.5 vertically\n"
    -1/0: 0.5 1.5                  # same with explicit datatype\n"
    -1: zstop=1.5, zstart=0.5      # same with named parameters\n"
    -1: height=1.0, zstop=1.5      # same with z stop minus height\n"
    -1: 1.0 zstop=1.5              # same with height as unnamed parameter\n"
    -  
    - -

    Variables

    - -

    - You can declare variables inside the setup files and use them in formulas for - computed values. Variables are defined and set with the "var" keyword on a single line. - The notation follows the "expression" syntax used in many other places inside KLayout - (). -

    - -

    - Here is an example: -

    - -
    var hmetal = 0.48\n"
    -7/0: 0.5 0.5+hmetal*2        # 2x thick metal\n"
    +  
    +z(layer, 0.1 .. 0.2)                extrude layer to z = 0.1 to 0.2 um
    +z(layer, zstart: 0.1, zstop: 0.2)   same as above
    +z(layer, zstart: 0.1, height: 0.1)  same as above, but with height instead of zstop
    +z(layer, height: 200.nm)            extrude layer from last z position with a height of 200nm
     
    -

    Conditionals

    -

    - For more flexibility, but of little practical use for now, conditionals are provided. - "if", "else", "elsif" and "end" for as in other languages, e.g. Ruby: + Examples for display options:

    -
    var thick_m1 = true
    -if thickm1
    -  1: 0.5 1.5
    -else
    -  1: 0.5 1.2
    +  
    +z(..., color: 0xff0000)             use bright red for the material color (RGB)
    +z(..., frame: 0xff0000)             use bright red for the frame color (combine with "fill" for the fill color)
    +z(..., fill: 0x00ff00)              use bright green for the fill color along (combine with "frame" for the frame color)
    +z(..., like: "7/0")                 borrow style from layout view's style for layer "7/0"
    +z(..., name: "M1")                  assigns a name to show for the material 
    +
    + +

    "zz" Function (material groups)

    + + +

    + The "zz" function forms a display group which clusters multiple "z" calls. The basic usage is with a block + containing the "z" calls. As DRC scripts are Ruby, the notation for the block is either "do .. end" or + curly brackets immediately after the "zz" call: +

    + +
    +zz( display options ... ) do
    +  z(layer1, extrusion options ... )
    +  z(layer2, extrusion options ... )
    +  ...
     end
     
    +

    + The "z" calls do not need to have colors or other display options as they are + taken from "zz". +

    + +

    + Material groups allow forming more complex, stacked geometries. Here is an example + forming a simple FinFET geometry using boolean and a sizing operation: +

    + +
    +poly = input(2, 0)
    +active = input(1, 0)
    +
    +z(poly, zstart: 0, height: 20.nm, name: "POLY")
    +
    +zz(name: "ACTIVE", like: "1/0") do
    +  
    +  poly_sized = poly.sized(10.nm)
    +  active_over_poly_sized = poly_sized & active
    +  
    +  z(active - poly, zstart: 0, height: 10.nm)       # bottom sheet
    +  z(active_over_poly_sized - poly, height: 10.nm)  # center sheet
    +  z(active_over_poly_sized, height: 10.nm)         # top sheet
    +
    +end
    +
    + +

    + Which renders this result: +

    + +

    + +

    +

    Navigating the 2.5d View

    - navigation - 2.5d navigation + +

    The navigation is based on the movement of the camera while the scene is @@ -159,7 +249,7 @@ end made invisible in the 2.5d view.

    -

    Other controls

    +

    Other Controls

    The left zoom slider changes the overall scale factor. The right slider only changes the z (height) axis zoom factor. @@ -176,5 +266,13 @@ end front view, top view etc.

    +

    Material Visibility

    + +

    + Using the check boxes from the material view right of the scene view you can disable + materials, so they are no longer rendered. From the material list's context menu, + you can hide or show all materials or just the selected ones. +

    + diff --git a/src/lay/lay/layHelpResources.qrc b/src/lay/lay/layHelpResources.qrc index 06524e1f5..a853c8b3d 100644 --- a/src/lay/lay/layHelpResources.qrc +++ b/src/lay/lay/layHelpResources.qrc @@ -49,6 +49,7 @@ doc/about/packages.xml doc/about/25d_view.xml doc/about/25d_screenshot.png + doc/about/25d_screenshot2.png doc/manual/adjust_origin.xml From c9727c2e6074383fc3a9e73a4deaffd5e88b0c06 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 6 Mar 2022 23:10:34 +0100 Subject: [PATCH 19/24] Polishing --- src/plugins/tools/view_25d/lay_plugin/D25View.ui | 6 +++++- .../view_25d/lay_plugin/built-in-macros/_d25_engine.rb | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/plugins/tools/view_25d/lay_plugin/D25View.ui b/src/plugins/tools/view_25d/lay_plugin/D25View.ui index e59d3d4d6..9fa88b45a 100644 --- a/src/plugins/tools/view_25d/lay_plugin/D25View.ui +++ b/src/plugins/tools/view_25d/lay_plugin/D25View.ui @@ -376,6 +376,9 @@ + + 2 + @@ -510,7 +513,8 @@ - In order to use the 2.5d view you will need a script which generates the view. + In order to use the 2.5d view you will need a script which generates the view.<br/> +See here for more information: <a href="int:/about/25d_view.xml">The 2.5d View</a>. Qt::AlignCenter diff --git a/src/plugins/tools/view_25d/lay_plugin/built-in-macros/_d25_engine.rb b/src/plugins/tools/view_25d/lay_plugin/built-in-macros/_d25_engine.rb index 54949b9a3..e1f1a9654 100644 --- a/src/plugins/tools/view_25d/lay_plugin/built-in-macros/_d25_engine.rb +++ b/src/plugins/tools/view_25d/lay_plugin/built-in-macros/_d25_engine.rb @@ -108,6 +108,9 @@ module D25 raise("Duplicate layer argument") end layer = a + if ! layer.data.is_a?(RBA::Region) + raise("Expected a polygon layer") + end elsif a.is_a?(1.class) || a.is_a?(1.0.class) From 553d973b69690402218f174ffef7c0de50398a7a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 7 Mar 2022 21:21:43 +0100 Subject: [PATCH 20/24] Edge and edge pair support --- src/lay/lay/doc/about/25d_view.xml | 2 +- .../lay_plugin/built-in-macros/_d25_engine.rb | 4 +- .../view_25d/lay_plugin/gsiDeclLayD25View.cc | 6 + .../tools/view_25d/lay_plugin/layD25View.cc | 16 +++ .../tools/view_25d/lay_plugin/layD25View.h | 4 + .../view_25d/lay_plugin/layD25ViewWidget.cc | 128 ++++++++++++------ .../view_25d/lay_plugin/layD25ViewWidget.h | 10 +- 7 files changed, 125 insertions(+), 45 deletions(-) diff --git a/src/lay/lay/doc/about/25d_view.xml b/src/lay/lay/doc/about/25d_view.xml index 48a399e15..22438889e 100644 --- a/src/lay/lay/doc/about/25d_view.xml +++ b/src/lay/lay/doc/about/25d_view.xml @@ -82,7 +82,7 @@ z(input(2, 0), height: 300.nm) # adds layer 2/0 for the next 30
    • z(layer [, options])

      -

      Extrudes the given layer. "layer" is a (polygon) DRC layer. "options" declare the z extrusion and display parameters.

      +

      Extrudes the given layer. "layer" is a DRC layer (polygon, edge or even edge pair). "options" declare the z extrusion and display parameters.

    • zz( [options] ) { block }

      Declares a material group which combines multiple "z" statements under a single display group. diff --git a/src/plugins/tools/view_25d/lay_plugin/built-in-macros/_d25_engine.rb b/src/plugins/tools/view_25d/lay_plugin/built-in-macros/_d25_engine.rb index e1f1a9654..f1114cc0b 100644 --- a/src/plugins/tools/view_25d/lay_plugin/built-in-macros/_d25_engine.rb +++ b/src/plugins/tools/view_25d/lay_plugin/built-in-macros/_d25_engine.rb @@ -108,8 +108,8 @@ module D25 raise("Duplicate layer argument") end layer = a - if ! layer.data.is_a?(RBA::Region) - raise("Expected a polygon layer") + if ! layer.data.is_a?(RBA::Region) && ! layer.data.is_a?(RBA::Edges) && ! layer.data.is_a?(RBA::EdgePairs) + raise("Expected a polygon, edge or edge pair layer") end elsif a.is_a?(1.class) || a.is_a?(1.0.class) diff --git a/src/plugins/tools/view_25d/lay_plugin/gsiDeclLayD25View.cc b/src/plugins/tools/view_25d/lay_plugin/gsiDeclLayD25View.cc index 4f614c1ce..ea7bc880e 100644 --- a/src/plugins/tools/view_25d/lay_plugin/gsiDeclLayD25View.cc +++ b/src/plugins/tools/view_25d/lay_plugin/gsiDeclLayD25View.cc @@ -71,6 +71,12 @@ Class decl_D25View (QT_EXTERNAL_BASE (QDialog) "lay", "D25View", gsi::method ("entry", &lay::D25View::entry, gsi::arg ("data"), gsi::arg ("dbu"), gsi::arg ("zstart"), gsi::arg ("zstop"), "@brief Creates a new display entry in the group opened with \\open_display" ) + + gsi::method ("entry", &lay::D25View::entry_edge, gsi::arg ("data"), gsi::arg ("dbu"), gsi::arg ("zstart"), gsi::arg ("zstop"), + "@brief Creates a new display entry in the group opened with \\open_display" + ) + + gsi::method ("entry", &lay::D25View::entry_edge_pair, gsi::arg ("data"), gsi::arg ("dbu"), gsi::arg ("zstart"), gsi::arg ("zstop"), + "@brief Creates a new display entry in the group opened with \\open_display" + ) + gsi::method ("close_display", &lay::D25View::close_display, "@brief Finishes the display group" ) + diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc index 91fb79a5c..e01ad1507 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc @@ -221,6 +221,22 @@ D25View::entry (const db::Region &data, double dbu, double zstart, double zstop) } } +void +D25View::entry_edge (const db::Edges &data, double dbu, double zstart, double zstop) +{ + if (! mp_ui->d25_view->has_error ()) { + mp_ui->d25_view->entry (data, dbu, zstart, zstop); + } +} + +void +D25View::entry_edge_pair (const db::EdgePairs &data, double dbu, double zstart, double zstop) +{ + if (! mp_ui->d25_view->has_error ()) { + mp_ui->d25_view->entry (data, dbu, zstart, zstop); + } +} + static void layer_info_to_item (const lay::D25ViewWidget::LayerInfo &info, QListWidgetItem *item, size_t index, QSize icon_size) { if (info.has_name) { diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.h b/src/plugins/tools/view_25d/lay_plugin/layD25View.h index e28cd447a..724c14e25 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25View.h +++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.h @@ -43,6 +43,8 @@ namespace lay namespace db { class Region; + class Edges; + class EdgePairs; struct LayerProperties; } @@ -69,6 +71,8 @@ public: void open_display (const color_t *frame_color, const color_t *fill_color, const db::LayerProperties *like, const std::string *name); void close_display (); void entry (const db::Region &data, double dbu, double zstart, double zstop); + void entry_edge (const db::Edges &data, double dbu, double zstart, double zstop); + void entry_edge_pair (const db::EdgePairs &data, double dbu, double zstart, double zstop); void finish (); protected: diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index 565f5721a..131119032 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -31,7 +31,11 @@ #include "dbPolygonTools.h" #include "dbClip.h" #include "dbRegion.h" +#include "dbEdges.h" +#include "dbEdgePairs.h" #include "dbOriginalLayerRegion.h" +#include "dbOriginalLayerEdges.h" +#include "dbOriginalLayerEdgePairs.h" #include "tlException.h" #include "tlProgress.h" @@ -605,7 +609,7 @@ D25ViewWidget::close_display () } void -D25ViewWidget::entry (const db::Region &data, double dbu, double zstart, double zstop) +D25ViewWidget::enter (const db::RecursiveShapeIterator *iter, double zstart, double zstop) { tl_assert (m_display_open); @@ -621,12 +625,10 @@ D25ViewWidget::entry (const db::Region &data, double dbu, double zstart, double LayerInfo &info = m_layers.back (); // try to establish a default color from the region's origin if required - const db::OriginalLayerRegion *original_region = dynamic_cast (data.delegate ()); if (mp_view && info.fill_color [3] == 0.0 && info.frame_color [3] == 0.0) { - if (original_region) { + if (iter) { - const db::RecursiveShapeIterator *iter = original_region->iter (); if (iter && iter->layout () && iter->layout ()->is_valid_layer (iter->layer ())) { db::LayerProperties like = iter->layout ()->get_properties (iter->layer ()); @@ -653,11 +655,56 @@ D25ViewWidget::entry (const db::Region &data, double dbu, double zstart, double } } +} + +void +D25ViewWidget::entry (const db::Region &data, double dbu, double zstart, double zstop) +{ + // try to establish a default color from the region's origin if required + const db::RecursiveShapeIterator *iter = 0; + const db::OriginalLayerRegion *original = dynamic_cast (data.delegate ()); + if (original) { + iter = original->iter (); + } + + enter (iter, zstart, zstop); tl::AbsoluteProgress progress (tl::to_string (tr ("Rendering ..."))); render_region (progress, *m_layers.back ().vertex_chunk, *m_layers.back ().line_chunk, data, dbu, db::CplxTrans (dbu).inverted () * m_bbox, zstart, zstop); } +void +D25ViewWidget::entry (const db::Edges &data, double dbu, double zstart, double zstop) +{ + // try to establish a default color from the region's origin if required + const db::RecursiveShapeIterator *iter = 0; + const db::OriginalLayerEdges *original = dynamic_cast (data.delegate ()); + if (original) { + iter = original->iter (); + } + + enter (iter, zstart, zstop); + + tl::AbsoluteProgress progress (tl::to_string (tr ("Rendering ..."))); + render_edges (progress, *m_layers.back ().vertex_chunk, *m_layers.back ().line_chunk, data, dbu, db::CplxTrans (dbu).inverted () * m_bbox, zstart, zstop); +} + +void +D25ViewWidget::entry (const db::EdgePairs &data, double dbu, double zstart, double zstop) +{ + // try to establish a default color from the region's origin if required + const db::RecursiveShapeIterator *iter = 0; + const db::OriginalLayerEdgePairs *original = dynamic_cast (data.delegate ()); + if (original) { + iter = original->iter (); + } + + enter (iter, zstart, zstop); + + tl::AbsoluteProgress progress (tl::to_string (tr ("Rendering ..."))); + render_edge_pairs (progress, *m_layers.back ().vertex_chunk, *m_layers.back ().line_chunk, data, dbu, db::CplxTrans (dbu).inverted () * m_bbox, zstart, zstop); +} + void D25ViewWidget::finish () { @@ -750,42 +797,6 @@ D25ViewWidget::render_wall (D25ViewWidget::triangle_chunks_type &chunks, D25View line_chunks.add (edge.p1 ().x () * dbu, zstop, edge.p1 ().y () * dbu); } -// @@@ -#if 0 -void -D25ViewWidget::render_layout (tl::AbsoluteProgress &progress, D25ViewWidget::triangle_chunks_type &chunks, D25ViewWidget::line_chunks_type &line_chunks, const db::Layout &layout, const db::Cell &cell, const db::Box &clip_box, unsigned int layer, double zstart, double zstop) -{ - std::vector poly_heap; - - // TODO: hidden cells, hierarchy depth ... - - db::RecursiveShapeIterator s (layout, cell, layer, clip_box); - s.shape_flags (db::ShapeIterator::Polygons | db::ShapeIterator::Paths | db::ShapeIterator::Boxes); - for ( ; ! s.at_end (); ++s) { - - db::Polygon polygon; - s->polygon (polygon); - polygon.transform (s.trans ()); - - poly_heap.clear (); - db::clip_poly (polygon, clip_box, poly_heap, false /*keep holes*/); - - for (std::vector::const_iterator p = poly_heap.begin (); p != poly_heap.end (); ++p) { - - ++progress; - - render_polygon (chunks, line_chunks, *p, layout.dbu (), zstart, zstop); - - for (db::Polygon::polygon_edge_iterator e = p->begin_edge (); ! e.at_end (); ++e) { - render_wall (chunks, line_chunks, *e, layout.dbu (), zstart, zstop); - } - - } - - } -} -#endif - void D25ViewWidget::render_region (tl::AbsoluteProgress &progress, D25ViewWidget::triangle_chunks_type &chunks, D25ViewWidget::line_chunks_type &line_chunks, const db::Region ®ion, double dbu, const db::Box &clip_box, double zstart, double zstop) { @@ -811,6 +822,43 @@ D25ViewWidget::render_region (tl::AbsoluteProgress &progress, D25ViewWidget::tri } } +void +D25ViewWidget::render_edges (tl::AbsoluteProgress &progress, D25ViewWidget::triangle_chunks_type &chunks, D25ViewWidget::line_chunks_type &line_chunks, const db::Edges &edges, double dbu, const db::Box &clip_box, double zstart, double zstop) +{ + for (db::Edges::const_iterator e = edges.begin (); !e.at_end (); ++e) { + + ++progress; + + std::pair ec = e->clipped (clip_box); + if (ec.first) { + render_wall (chunks, line_chunks, ec.second, dbu, zstart, zstop); + } + + } +} + +void +D25ViewWidget::render_edge_pairs (tl::AbsoluteProgress &progress, D25ViewWidget::triangle_chunks_type &chunks, D25ViewWidget::line_chunks_type &line_chunks, const db::EdgePairs &edge_pairs, double dbu, const db::Box &clip_box, double zstart, double zstop) +{ + for (db::EdgePairs::const_iterator e = edge_pairs.begin (); !e.at_end (); ++e) { + + ++progress; + + std::pair ec; + + ec = e->first ().clipped (clip_box); + if (ec.first) { + render_wall (chunks, line_chunks, ec.second, dbu, zstart, zstop); + } + + ec = e->second ().clipped (clip_box); + if (ec.first) { + render_wall (chunks, line_chunks, ec.second, dbu, zstart, zstop); + } + + } +} + static std::pair find_grid (double v) { for (int p = -12; p < 12; ++p) { diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h index a553f8122..843a41681 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h @@ -43,6 +43,9 @@ namespace db { class Region; + class Edges; + class EdgePairs; + class RecursiveShapeIterator; struct LayerProperties; } @@ -160,6 +163,8 @@ public: void open_display (const color_t *frame_color, const color_t *fill_color, const db::LayerProperties *like, const std::string *name); void close_display (); void entry (const db::Region &data, double dbu, double zstart, double zstop); + void entry (const db::Edges &data, double dbu, double zstart, double zstop); + void entry (const db::EdgePairs &data, double dbu, double zstart, double zstop); void finish (); signals: @@ -200,12 +205,13 @@ private: void resizeGL (int w, int h); void do_initialize_gl (); - // @@@ bool prepare_view(); - // @@@ void render_layout (tl::AbsoluteProgress &progress, D25ViewWidget::triangle_chunks_type &vertex_chunks, D25ViewWidget::line_chunks_type &line_chunks, const db::Layout &layout, const db::Cell &cell, const db::Box &clip_box, unsigned int layer, double zstart, double zstop); void render_region (tl::AbsoluteProgress &progress, D25ViewWidget::triangle_chunks_type &vertex_chunks, D25ViewWidget::line_chunks_type &line_chunks, const db::Region ®ion, double dbu, const db::Box &clip_box, double zstart, double zstop); + void render_edges (tl::AbsoluteProgress &progress, D25ViewWidget::triangle_chunks_type &vertex_chunks, D25ViewWidget::line_chunks_type &line_chunks, const db::Edges ®ion, double dbu, const db::Box &clip_box, double zstart, double zstop); + void render_edge_pairs (tl::AbsoluteProgress &progress, D25ViewWidget::triangle_chunks_type &vertex_chunks, D25ViewWidget::line_chunks_type &line_chunks, const db::EdgePairs ®ion, double dbu, const db::Box &clip_box, double zstart, double zstop); void render_polygon (D25ViewWidget::triangle_chunks_type &vertex_chunks, D25ViewWidget::line_chunks_type &line_chunks, const db::Polygon &poly, double dbu, double zstart, double zstop); void render_wall (D25ViewWidget::triangle_chunks_type &vertex_chunks, D25ViewWidget::line_chunks_type &line_chunks, const db::Edge &poly, double dbu, double zstart, double zstop); void reset_viewport (); + void enter (const db::RecursiveShapeIterator *iter, double zstart, double zstop); }; } From a0cbc2355aaaeb96f52c68674c7f53da87bc41dd Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 7 Mar 2022 21:49:48 +0100 Subject: [PATCH 21/24] Some cleanup --- src/gtfui/gtfUiDialog.cc | 2 +- src/plugins/tools/view_25d/lay_plugin/D25View.ui | 3 +++ src/plugins/tools/view_25d/lay_plugin/layD25View.cc | 6 +++--- src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc | 1 - src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h | 2 -- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/gtfui/gtfUiDialog.cc b/src/gtfui/gtfUiDialog.cc index 395dc2eec..504760616 100644 --- a/src/gtfui/gtfUiDialog.cc +++ b/src/gtfui/gtfUiDialog.cc @@ -264,7 +264,7 @@ static std::string log_event_to_text (const gtf::LogEventBase *e) { std::string t = e->name (); - /* @@@ too much: + /* too much: std::vector< std::pair > attrs; e->attributes (attrs); for (std::vector< std::pair >::const_iterator a = attrs.begin (); a != attrs.end (); ++a) { diff --git a/src/plugins/tools/view_25d/lay_plugin/D25View.ui b/src/plugins/tools/view_25d/lay_plugin/D25View.ui index 9fa88b45a..a38817f62 100644 --- a/src/plugins/tools/view_25d/lay_plugin/D25View.ui +++ b/src/plugins/tools/view_25d/lay_plugin/D25View.ui @@ -61,6 +61,9 @@ + + false + Execute script again diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc index e01ad1507..019dedecd 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc @@ -68,9 +68,8 @@ D25View::D25View (lay::Dispatcher *root, lay::LayoutView *view) connect (mp_ui->show_all_action, SIGNAL (triggered()), this, SLOT (show_all_triggered())); connect (mp_ui->show_selected_action, SIGNAL (triggered()), this, SLOT (show_selected_triggered())); - mp_ui->rerun_button->setEnabled (false); - mp_ui->gl_stack->setCurrentIndex (2); + mp_ui->rerun_button->setEnabled (false); lay::activate_help_links (mp_ui->doc_label); lay::activate_help_links (mp_ui->empty_label); @@ -193,7 +192,6 @@ D25View::begin (const std::string &generator) if (! mp_ui->d25_view->has_error ()) { m_generator = generator; - mp_ui->rerun_button->setEnabled (true); } } @@ -293,6 +291,7 @@ D25View::finish () // NOTE: needs to be delayed to allow the geometry to be updated before (initial call) dm_fit (); + mp_ui->rerun_button->setEnabled (true); mp_ui->gl_stack->setCurrentIndex (0); } @@ -314,6 +313,7 @@ D25View::init_failed () { mp_ui->error_text->setPlainText (tl::to_qstring (mp_ui->d25_view->error ())); mp_ui->gl_stack->setCurrentIndex (1); + mp_ui->rerun_button->setEnabled (false); } void diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index 131119032..94e7f2705 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -517,7 +517,6 @@ void D25ViewWidget::clear () { m_layers.clear (); - m_layer_to_info.clear (); m_vertex_chunks.clear (); m_line_chunks.clear (); diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h index 843a41681..af5dd0a54 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h @@ -107,7 +107,6 @@ public: void mouseMoveEvent (QMouseEvent *event); void attach_view(lay::LayoutView *view); - // @@@ void refresh_view (); QVector3D hit_point_with_scene(const QVector3D &line_dir); void refresh (); @@ -198,7 +197,6 @@ private: std::list m_line_chunks; std::vector m_layers; - std::multimap, size_t> m_layer_to_info; // @@@ void initializeGL (); void paintGL (); From c5da93756a2ed4ef6c314413e9db560bc6650434 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 8 Mar 2022 16:31:53 +0100 Subject: [PATCH 22/24] Fixed build with Qt bindings (maybe) --- src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro b/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro index aeac20510..d0051dd62 100644 --- a/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro +++ b/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro @@ -4,9 +4,9 @@ DESTDIR = $$OUT_PWD/../../../../lay_plugins include($$PWD/../../../lay_plugin.pri) -INCLUDEPATH += $$RDB_INC $$ANT_INC -DEPENDPATH += $$RDB_INC $$ANT_INC -LIBS += -L$$DESTDIR/.. -lklayout_rdb -lklayout_ant +INCLUDEPATH += $$RDB_INC $$ANT_INC $$QTBASIC_INC +DEPENDPATH += $$RDB_INC $$ANT_INC $$QTBASIC_INC +LIBS += -L$$DESTDIR/.. -lklayout_qtbasic -lklayout_rdb -lklayout_ant HEADERS = \ layD25View.h \ From 3b2db3b379798b8c197a96c3ad35ef76e1e65eb4 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 9 Mar 2022 23:00:24 +0100 Subject: [PATCH 23/24] Fixed build without Qt bindings --- src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro b/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro index d0051dd62..220aa3cf8 100644 --- a/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro +++ b/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro @@ -6,7 +6,11 @@ include($$PWD/../../../lay_plugin.pri) INCLUDEPATH += $$RDB_INC $$ANT_INC $$QTBASIC_INC DEPENDPATH += $$RDB_INC $$ANT_INC $$QTBASIC_INC -LIBS += -L$$DESTDIR/.. -lklayout_qtbasic -lklayout_rdb -lklayout_ant + +LIBS += -L$$DESTDIR/.. -lklayout_rdb -lklayout_ant +equals(HAVE_QTBINDINGS, "1") { + LIBS += -lklayout_qtbasic -lklayout_QtGui -lklayout_QtCore +} HEADERS = \ layD25View.h \ From 0e1e4781a83f027412741a1c7c66268156b44c6c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 9 Mar 2022 23:00:39 +0100 Subject: [PATCH 24/24] Fixed scale & snap bug in non-editable mode --- src/db/db/dbLayoutUtils.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/db/dbLayoutUtils.cc b/src/db/db/dbLayoutUtils.cc index 82ead48ce..dea4ab705 100644 --- a/src/db/db/dbLayoutUtils.cc +++ b/src/db/db/dbLayoutUtils.cc @@ -520,7 +520,7 @@ scale_and_snap (db::Layout &layout, db::Cell &cell, db::Coord g, db::Coord m, db for (db::Layout::layer_iterator l = layout.begin_layers (); l != layout.end_layers (); ++l) { db::Shapes &s = c->shapes ((*l).first); - db::Shapes new_shapes; + db::Shapes new_shapes (layout.is_editable ()); for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::Polygons | db::ShapeIterator::Paths | db::ShapeIterator::Boxes); ! si.at_end (); ++si) {