Merge branch '25d-with-booleans'

This commit is contained in:
Matthias Koefferlein 2022-03-05 14:59:17 +01:00
commit cd07a21ae3
27 changed files with 1199 additions and 120 deletions

View File

@ -511,19 +511,52 @@ VariantsCollectorBase::create_var_instances_non_tl_invariant (db::Cell &in_cell,
std::map<db::cell_index_type, std::map<db::ICplxTrans, db::cell_index_type> >::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<db::ICplxTrans, db::cell_index_type> &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<db::ICplxTrans, db::cell_index_type>::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<db::ICplxTrans, db::cell_index_type>::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);
}

View File

@ -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<db::CellInst> (*mp_subject_layout)).to_string()).c_str(),
mp_intruder_layout->cell_name (inst2->object ().cell_index ()), int (inst2->size ()), (inst2->bbox(db::box_convert<db::CellInst> (*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<db::ICplxTrans, std::list<std::pair<db::cell_index_type, db::ICplxTrans> > > 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<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
const typename local_processor_cell_contexts<TS, TI, TR>::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) {

View File

@ -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<db::Edge, db::properties_id_type, EdgeCompareOpWithTolerance, std_compare_func<db::properties_id_type> > (EdgeCompareOpWithTolerance (tolerance), std_compare_func<db::properties_id_type> ());
}
pair_compare_func<db::EdgePair, db::properties_id_type, EdgePairCompareOpWithTolerance, std_compare_func<db::properties_id_type> >
make_edge_pair_compare_func (db::Coord tolerance)
{
return pair_compare_func<db::EdgePair, db::properties_id_type, EdgePairCompareOpWithTolerance, std_compare_func<db::properties_id_type> > (EdgePairCompareOpWithTolerance (tolerance), std_compare_func<db::properties_id_type> ());
}
pair_compare_func<db::Box, db::properties_id_type, BoxCompareOpWithTolerance, std_compare_func<db::properties_id_type> >
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<db::EdgePair, db::properties_id_type> > &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<db::Box, db::properties_id_type> > &shapes, PropertyMapper &pn)
{
@ -828,6 +875,8 @@ do_compare_layouts (const db::Layout &a, const db::Cell *top_a, const db::Layout
std::vector <std::pair <db::Box, db::properties_id_type> > boxes_b;
std::vector <std::pair <db::Edge, db::properties_id_type> > edges_a;
std::vector <std::pair <db::Edge, db::properties_id_type> > edges_b;
std::vector <std::pair <db::EdgePair, db::properties_id_type> > edge_pairs_a;
std::vector <std::pair <db::EdgePair, db::properties_id_type> > 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 <std::pair <db::Edge, db::properties_id_type> > &a, const std::vector <std::pair <db::Edge, db::properties_id_type> > &b);
void end_edge_differences ();
void begin_edge_pair_differences ();
void detailed_diff (const db::PropertiesRepository &pr, const std::vector <std::pair <db::EdgePair, db::properties_id_type> > &a, const std::vector <std::pair <db::EdgePair, db::properties_id_type> > &b);
void end_edge_pair_differences ();
void begin_text_differences ();
void detailed_diff (const db::PropertiesRepository &pr, const std::vector <std::pair <db::Text, db::properties_id_type> > &a, const std::vector <std::pair <db::Text, db::properties_id_type> > &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 <std::pair <db::EdgePair, db::properties_id_type> > &a, const std::vector <std::pair <db::EdgePair, db::properties_id_type> > &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 ()
{

View File

@ -119,6 +119,9 @@ public:
virtual void begin_edge_differences () { }
virtual void detailed_diff (const db::PropertiesRepository & /*pr*/, const std::vector <std::pair <db::Edge, db::properties_id_type> > & /*a*/, const std::vector <std::pair <db::Edge, db::properties_id_type> > & /*b*/) { }
virtual void end_edge_differences () { }
virtual void begin_edge_pair_differences () { }
virtual void detailed_diff (const db::PropertiesRepository & /*pr*/, const std::vector <std::pair <db::EdgePair, db::properties_id_type> > & /*a*/, const std::vector <std::pair <db::EdgePair, db::properties_id_type> > & /*b*/) { }
virtual void end_edge_pair_differences () { }
virtual void begin_text_differences () { }
virtual void detailed_diff (const db::PropertiesRepository & /*pr*/, const std::vector <std::pair <db::Text, db::properties_id_type> > & /*a*/, const std::vector <std::pair <db::Text, db::properties_id_type> > & /*b*/) { }
virtual void end_text_differences () { }

View File

@ -25,6 +25,7 @@
#include "dbCellVariants.h"
#include "dbPolygonTools.h"
#include "tlProgress.h"
#include "tlTimer.h"
namespace db
{
@ -436,9 +437,38 @@ ContextCache::find_layout_context (db::cell_index_type from, db::cell_index_type
// ------------------------------------------------------------
// Scale and snap a layout
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 +483,11 @@ scale_and_snap (db::Layout &layout, db::Cell &cell, db::Coord g, db::Coord m, db
db::cell_variants_collector<db::ScaleAndGridReducer> 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<db::cell_index_type> called_cells;
cell.collect_called_cells (called_cells);
@ -463,9 +496,10 @@ 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<db::Point> 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<db::Point> heap;
std::vector<db::Vector> iterated_array_vectors;
for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) {
@ -486,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 &out = c->shapes (work_layer);
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) {
@ -495,7 +529,20 @@ 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);
if (si->is_box () && poly.is_box ()) {
if (si->has_prop_id ()) {
new_shapes.insert (db::BoxWithProperties (poly.box (), si->prop_id ()));
} else {
new_shapes.insert (poly.box ());
}
} else {
if (si->has_prop_id ()) {
new_shapes.insert (db::PolygonWithProperties (poly, si->prop_id ()));
} else {
new_shapes.insert (poly);
}
}
}
@ -506,48 +553,94 @@ 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);
if (si->has_prop_id ()) {
new_shapes.insert (db::TextWithProperties (text, si->prop_id ()));
} else {
new_shapes.insert (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);
if (si->has_prop_id ()) {
new_shapes.insert (db::EdgeWithProperties (edge, si->prop_id ()));
} else {
new_shapes.insert (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);
if (si->has_prop_id ()) {
new_shapes.insert (db::EdgePairWithProperties (edge_pair, si->prop_id ()));
} else {
new_shapes.insert (edge_pair);
}
}
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.
std::list<db::CellInstArray> new_insts;
// 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 ();
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);
iterated_array_vectors.clear ();
db::Vector a, b;
unsigned long na, nb;
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));
db::CellInstArray new_array (ia);
if (ia.is_iterated_array (&iterated_array_vectors)) {
bool needs_update = false;
for (std::vector<db::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 (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)) {
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 ());
new_array = db::CellInstArray (ia.object (), ia.complex_trans (ia.front ()), a, b, na, nb);
}
}
scale_and_snap_cell_instance (new_array, tr, trinv, tr_disp, g, m, d);
c->replace (*inst, new_array);
c->clear_insts ();
for (std::list<db::CellInstArray>::const_iterator i = new_insts.begin (); i != new_insts.end (); ++i) {
c->insert (*i);
}
}

View File

@ -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) {

View File

@ -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
};
/**

View File

@ -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<db::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));
}

View File

@ -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
</text>

View File

@ -100,7 +100,6 @@
<widget class="QLabel" name="interpreterLabel">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
@ -224,25 +223,56 @@
<number>6</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Priority</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Prolog</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="prolog"/>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="epilog"/>
</item>
<item row="1" column="0">
<item row="2" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Epilog</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="priority">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_12">
<property name="text">
<string>for autorun: 0 = first, 1 = second ...</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="QLineEdit" name="prolog">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="2" column="1" colspan="2">
<widget class="QLineEdit" name="epilog"/>
</item>
</layout>
</widget>
</item>

View File

@ -87,6 +87,12 @@ static std::string version (C *)
return C::version ();
}
template <class C>
static void add_macro_category (C *c, const std::string &name, const std::string &description, const std::vector<std::string> &folders)
{
c->add_macro_category (name, description, folders);
}
template <class C>
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<C, const std::string &, const std::string &, const std::vector<std::string> &> ("add_macro_category", &add_macro_category<C>, 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<C *> ("instance", &C::instance,
"@brief Return the singleton instance of the application\n"
"\n"

View File

@ -686,6 +686,22 @@ ApplicationBase::init_app ()
if (mc) {
// create the basic macro categories
if (ruby_interpreter ().available ()) {
std::vector<std::string> folders;
folders.push_back ("macros");
folders.push_back ("ruby");
mc->add_macro_category ("macros", "Ruby", folders);
}
if (python_interpreter ().available ()) {
std::vector<std::string> 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<std::string> 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<std::string> &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);

View File

@ -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<std::string> &folders);
/**
* @brief Return true, if undo buffering is enabled
*/

View File

@ -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<std::string> &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 ();

View File

@ -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<std::string> &folders);
/**
* @brief Adds a temporary macro
*

View File

@ -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);
}
}

View File

@ -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
</text>
</klayout-macro>

View File

@ -46,6 +46,8 @@
#include <fstream>
#include <memory>
#include <string>
#include <set>
namespace lym
{
@ -53,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 ..
}
@ -87,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;
@ -111,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 &&
@ -182,6 +186,7 @@ static tl::XMLStruct<lym::Macro> 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") +
@ -550,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)
@ -623,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));
}
}
}
@ -670,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);
}
}
@ -694,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;
@ -765,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) {
@ -1065,6 +1093,11 @@ MacroCollection::MacroCollection ()
}
MacroCollection::~MacroCollection ()
{
do_clear ();
}
void MacroCollection::do_clear ()
{
for (iterator m = begin (); m != end (); ++m) {
delete m->second;
@ -1513,6 +1546,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 +1901,78 @@ bool MacroCollection::has_autorun_early () const
return has_autorun_for (*this, true);
}
static void autorun_for (lym::MacroCollection &collection, bool early)
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) {
autorun_for (*c->second, early);
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 ()))) {
BEGIN_PROTECTED_SILENT
c->second->run ();
c->second->install_doc ();
END_PROTECTED_SILENT
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<std::string> *executed_already, int prio)
{
for (lym::MacroCollection::child_iterator c = collection.begin_children (); c != collection.end_children (); ++c) {
autorun_for_prio (*c->second, early, executed_already, prio);
}
for (lym::MacroCollection::iterator c = collection.begin (); c != collection.end (); ++c) {
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 ()) {
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 ()
static void autorun_for (lym::MacroCollection &collection, bool early, std::set<std::string> *executed_already)
{
autorun_for (*this, false);
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_early ()
void MacroCollection::autorun (std::set<std::string> *already_executed)
{
autorun_for (*this, true);
autorun_for (*this, false, already_executed);
}
void MacroCollection::autorun_early (std::set<std::string> *already_executed)
{
autorun_for (*this, true, already_executed);
}
void MacroCollection::dump (int l)

View File

@ -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;
@ -845,6 +860,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 +1016,7 @@ public:
/**
* @brief Runs all macros marked with auto-run
*/
void autorun ();
void autorun (std::set<std::string> *already_executed = 0);
/**
* @brief Returns true, if the collection has an early autorun macro
@ -1005,7 +1026,7 @@ public:
/**
* @brief Runs all macros marked with early auto-run
*/
void autorun_early ();
void autorun_early (std::set<std::string> *already_executed = 0);
/**
* @brief Redo the scan (will add new files or folders)
@ -1129,6 +1150,8 @@ private:
m_readonly = f;
}
void do_clear ();
// no copying
MacroCollection (const MacroCollection &d);
MacroCollection &operator= (const MacroCollection &d);

View File

@ -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

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<klayout-macro>
<description/>
<version/>
<category/>
<prolog/>
<epilog/>
<doc/>
<autorun>true</autorun>
<autorun-early>false</autorun-early>
<shortcut/>
<show-in-menu>false</show-in-menu>
<group-name/>
<menu-path/>
<interpreter>ruby</interpreter>
<dsl-interpreter-name/>
<text>
module D25
# Installs the home menu entries (needs to be done on autorun, not autorun-early)
if RBA::Application::instance &amp;&amp; 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
</text>
</klayout-macro>

View File

@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<klayout-macro>
<description/>
<version/>
<category/>
<prolog/>
<epilog/>
<doc/>
<autorun>false</autorun>
<autorun-early>true</autorun-early>
<shortcut/>
<show-in-menu>false</show-in-menu>
<group-name/>
<menu-path/>
<interpreter>ruby</interpreter>
<dsl-interpreter-name/>
<text>
module D25
class D25Executable &lt; 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 &gt;= 10 &amp;&amp; RBA::Logger::info("Running #{@macro.path}")
@d25.instance_eval(@macro.text, @macro.path)
rescue =&gt; 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 &lt; 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 &amp;&amp; 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 &lt; 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 &lt; 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
</text>
</klayout-macro>

View File

@ -0,0 +1,10 @@
<RCC>
<qresource prefix="/built-in-macros">
<file alias="_d25_engine.rb">built-in-macros/_d25_engine.rb</file>
<file alias="d25_interpreters.lym">built-in-macros/d25_interpreters.lym</file>
<file alias="d25_install.lym">built-in-macros/d25_install.lym</file>
</qresource>
<qresource prefix="/d25-templates">
<file alias="d25.lym">templates/d25.lym</file>
</qresource>
</RCC>

View File

@ -26,6 +26,9 @@ SOURCES = \
FORMS = \
D25View.ui \
RESOURCES = \
layD25Resources.qrc \
greaterThan(QT_MAJOR_VERSION, 5) {
QT += openglwidgets
}

View File

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<klayout-macro>
<description>General;;2.5d view generator script (*.lyd25)\nA script generating a 2.5d view</description>
<version/>
<category>d25</category>
<prolog/>
<epilog/>
<doc/>
<autorun>false</autorun>
<autorun-early>false</autorun-early>
<shortcut/>
<show-in-menu>true</show-in-menu>
<group-name>d25_scripts</group-name>
<menu-path>tools_menu.d25.end</menu-path>
<interpreter>ruby</interpreter>
<dsl-interpreter-name/>
<text>
# 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
</text>
</klayout-macro>

BIN
testdata/algo/layout_utils_au_sns4.oas vendored Normal file

Binary file not shown.

BIN
testdata/algo/scale_and_snap4.oas vendored Normal file

Binary file not shown.