Merge pull request #1019 from KLayout/25d-with-booleans

25d with booleans
This commit is contained in:
Matthias Köfferlein 2022-03-10 19:15:16 +01:00 committed by GitHub
commit 9fc7f9cfb6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
50 changed files with 2326 additions and 1307 deletions

View File

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

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

@ -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<bool> conditional_stack;
std::vector<std::string> lines = tl::split (m_src, "\n");
for (std::vector<std::string>::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<double> 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<D25TechnologyComponent> (d25_component_name (),
tl::make_member (&D25TechnologyComponent::src, &D25TechnologyComponent::set_src, "src")
);
}
};
static tl::RegisteredClass<db::TechnologyComponentProvider> tc_decl (new D25TechnologyComponentProvider (), 3100, d25_component_name ().c_str ());
}

View File

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

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 (layout.is_editable ());
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

@ -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 (...) { }
}

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

@ -39,7 +39,6 @@ SOURCES = \
dbPolygonToolsTests.cc \
dbTechnologyTests.cc \
dbStreamLayerTests.cc \
dbD25TechnologyComponentTests.cc \
dbVectorTests.cc \
dbVariableWidthPathTests.cc \
dbTransTests.cc \

View File

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

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

@ -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<std::string, std::string> > attrs;
e->attributes (attrs);
for (std::vector< std::pair<std::string, std::string> >::const_iterator a = attrs.begin (); a != attrs.end (); ++a) {

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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

View File

@ -18,97 +18,187 @@
</p>
<p>
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.
</p>
<p>
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 <link href="/manual/drc.xml"/>).
The DRC language is used to import or generate polygon layers which are then
extruded and placed on a certain z level.
</p>
<p>
To create a new script, use "Tools/2.5d View/New 2.5d Script". This will create a new script in the
macro editor.
</p>
<p>
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:
</p>
<pre>
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
</pre>
<p>
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.
</p>
<p>
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.
</p>
<p>
<img src="/about/25d_screenshot.png"/>
</p>
<h2>Setup</h2>
<h2>2.5d Script Anatomy</h2>
<p>
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.
</p>
<p>
Each entry is a single line. Empty lines are ignored. Everything after a '#' character is
considered a comment.
</p>
<p>
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:
</p>
<ul>
<li> <b>zstart</b>: The lower z position of the extruded layer in µm </li>
<li> <b>zstop</b>: The upper z position of the extruded layer in µm </li>
<li> <b>height</b>: The height of the extruded layer in µm </li>
<li>You should not use external sources ("source" statement) as the 2.5d view is related to the loaded layout</li>
<li>Report generation or "output" statements are permitted, but do not make much sense in the context
of 2.5d view scripts.</li>
</ul>
<p>
'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:
</p>
<ul>
<li><p><tt>z(</tt>layer [, options]<tt>)</tt></p>
<p>Extrudes the given layer. "layer" is a DRC layer (polygon, edge or even edge pair). "options" declare the z extrusion and display parameters.</p>
</li>
<li><p><tt>zz(</tt> [options] <tt>) {</tt> block <tt>}</tt></p>
<p>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.</p>
</li>
</ul>
<h3>"z" Function (plane extrusion)</h3>
<keyword name="z"/>
<p>
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.
</p>
<p>
If a single unnamed parameter is given, it corresponds to 'height'. Two parameters correspond to
'zstart' and 'zstop'.
Options for this function are:
</p>
<ul>
<li><b>zstart</b>: specifies the bottom coordinate of the extruded sheet. If this option is not given, the top coordinate of the previous "z" statement is used.</li>
<li><b>zstop</b>: specifies the top coordinate of the extruded sheet. Alternatively you can use "height".</li>
<li><b>height</b>: specifies the extrusion height. Alternatively you can use "zstop".</li>
<li><b>color</b>: 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.</li>
<li><b>frame</b>: 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.</li>
<li><b>fill</b>: 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".</li>
<li><b>like</b>: 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).</li>
<li><b>name</b>: gives the material a name for displaying in the material list.</li>
</ul>
<p>
Here are some examples:
Examples for the extrusion options:
</p>
<pre>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"
</pre>
<h4>Variables</h4>
<p>
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
(<link href="/about/expressions.xml"/>).
</p>
<p>
Here is an example:
</p>
<pre>var hmetal = 0.48\n"
7/0: 0.5 0.5+hmetal*2 # 2x thick metal\n"
<pre>
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
</pre>
<h4>Conditionals</h4>
<p>
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:
</p>
<pre>var thick_m1 = true
if thickm1
1: 0.5 1.5
else
1: 0.5 1.2
<pre>
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
</pre>
<h3>"zz" Function (material groups)</h3>
<keyword name="zz"/>
<p>
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:
</p>
<pre>
zz( display options ... ) do
z(layer1, extrusion options ... )
z(layer2, extrusion options ... )
...
end
</pre>
<p>
The "z" calls do not need to have colors or other display options as they are
taken from "zz".
</p>
<p>
Material groups allow forming more complex, stacked geometries. Here is an example
forming a simple FinFET geometry using boolean and a sizing operation:
</p>
<pre>
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 &amp; 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
</pre>
<p>
Which renders this result:
</p>
<p>
<img src="/about/25d_screenshot2.png"/>
</p>
<h2>Navigating the 2.5d View</h2>
<keyword>navigation</keyword>
<keyword>2.5d navigation</keyword>
<keyword name="navigation"/>
<keyword name="2.5d view navigation"/>
<p>
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.
</p>
<h2>Other controls</h2>
<h2>Other Controls</h2>
<p>
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.
</p>
<h2>Material Visibility</h2>
<p>
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.
</p>
</doc>

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,6 +49,7 @@
<file alias="packages.xml">doc/about/packages.xml</file>
<file alias="25d_view.xml">doc/about/25d_view.xml</file>
<file alias="25d_screenshot.png">doc/about/25d_screenshot.png</file>
<file alias="25d_screenshot2.png">doc/about/25d_screenshot2.png</file>
</qresource>
<qresource prefix="/help/manual">
<file alias="adjust_origin.xml">doc/manual/adjust_origin.xml</file>

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

@ -1,55 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>D25TechnologyComponentEditor</class>
<widget class="QFrame" name="D25TechnologyComponentEditor">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>549</width>
<height>434</height>
</rect>
</property>
<property name="windowTitle">
<string>Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>&lt;html&gt;2.5d Vertical stack information (see &lt;a href=&quot;int:/about/25d_view.xml&quot;&gt;here&lt;/a&gt; for details)&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="lnum_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Line</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QTextEdit" name="src_te">
<property name="lineWrapMode">
<enum>QTextEdit::NoWrap</enum>
</property>
<property name="acceptRichText">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>

View File

@ -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 <QResource>
#include <QBuffer>
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 <db::D25TechnologyComponent *> (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 <db::D25TechnologyComponent *> (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<lay::TechnologyEditorProvider> editor_decl (new D25TechnologyComponentEditorProvider (), 3100, "d25");
} // namespace lay

View File

@ -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 <memory>
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<lay::GenericSyntaxHighlighterAttributes> mp_hl_attributes, mp_hl_basic_attributes;
};
}
#endif

View File

@ -35,8 +35,8 @@
namespace db
{
struct LayerProperties;
class Layout;
struct LayerProperties;
}
namespace lay

View File

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

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

@ -59,6 +59,42 @@
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QToolButton" name="rerun_button">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>Execute script again</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../../lay/lay/layResources.qrc">
<normaloff>:/run.png</normaloff>:/run.png</iconset>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>10</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QSlider" name="zoom_slider">
<property name="sizePolicy">
@ -343,6 +379,9 @@
</item>
<item>
<widget class="QStackedWidget" name="gl_stack">
<property name="currentIndex">
<number>2</number>
</property>
<widget class="QWidget" name="page">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
@ -358,7 +397,60 @@
<number>0</number>
</property>
<item>
<widget class="lay::D25ViewWidget" name="d25_view"/>
<widget class="QFrame" name="frame_3">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="lay::D25ViewWidget" name="d25_view">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
<widget class="QListWidget" name="material_list">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="spacing">
<number>0</number>
</property>
<property name="uniformItemSizes">
<bool>true</bool>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
@ -419,6 +511,21 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="page_3">
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QLabel" name="empty_label">
<property name="text">
<string>In order to use the 2.5d view you will need a script which generates the view.&lt;br/&gt;
See here for more information: &lt;a href=&quot;int:/about/25d_view.xml&quot;&gt;The 2.5d View&lt;/a&gt;.</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item>
@ -482,6 +589,36 @@
</widget>
</item>
</layout>
<action name="select_all_action">
<property name="text">
<string>Select All</string>
</property>
</action>
<action name="unselect_all_action">
<property name="text">
<string>Unselect All</string>
</property>
</action>
<action name="show_all_action">
<property name="text">
<string>Show All</string>
</property>
</action>
<action name="hide_all_action">
<property name="text">
<string>Hide All</string>
</property>
</action>
<action name="show_selected_action">
<property name="text">
<string>Show Selected</string>
</property>
</action>
<action name="hide_selected_action">
<property name="text">
<string>Hide Selected</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
@ -510,5 +647,37 @@
</hint>
</hints>
</connection>
<connection>
<sender>select_all_action</sender>
<signal>triggered()</signal>
<receiver>material_list</receiver>
<slot>selectAll()</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>716</x>
<y>336</y>
</hint>
</hints>
</connection>
<connection>
<sender>unselect_all_action</sender>
<signal>triggered()</signal>
<receiver>material_list</receiver>
<slot>clearSelection()</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>716</x>
<y>336</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,288 @@
# $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, :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
# The D25 engine
class D25Engine < DRC::DRCEngine
def initialize
super
@current_z = 0.0
@zstack = []
# clip to layout view
if ! RBA::LayoutView::current
raise "No layout loaded for running 2.5d view on"
end
self.region_overlap(RBA::LayoutView::current.box)
end
def z(*args)
self._context("z") do
layer = nil
zstart = nil
zstop = nil
height = nil
display = D25Display::new
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
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)
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
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
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, zstart, zstop, @display || display)
@zstack << info
return info
end
end
def zz(*args, &block)
begin
display = D25Display::new
@display = display
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)
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| ![ :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
else
raise("Argument not understood: #{a.inspect}")
end
end
block && 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)
super(final)
if final
view = RBA::LayoutView::current.open_d25_view
begin
view.begin(self._generator)
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, display.name)
zz.each do |z|
view.entry(z.layer.data, self.dbu, z.zstart, z.zstop)
end
view.close_display
end
view.finish
rescue => ex
view.clear
view.close
raise ex
end
end
end
end
end

View File

@ -0,0 +1,63 @@
<?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)
@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
</text>
</klayout-macro>

View File

@ -0,0 +1,171 @@
<?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)
@d25 = D25Engine::new
@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
@d25._check
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))
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,96 @@
/*
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 <limits>
#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<lay::LayoutView> 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<lay::D25View> 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"), 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"),
"@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"
) +
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"
"\n"
"This class has been introduced in version 0.28."
);
}

View File

@ -52,10 +52,9 @@ public:
return 0;
}
virtual void get_menu_entries (std::vector<lay::MenuEntry> &menu_entries) const
virtual void get_menu_entries (std::vector<lay::MenuEntry> & /*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*/)

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

@ -20,6 +20,8 @@
*/
#include "tlExceptions.h"
#include "tlRecipe.h"
#include "layD25View.h"
#include "layLayoutView.h"
@ -29,13 +31,17 @@
#include <stdio.h>
#include <QFontMetrics>
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),
dm_fit (this, &D25View::fit)
{
mp_ui = new Ui::D25View ();
mp_ui->setupUi (this);
@ -43,26 +49,52 @@ 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()));
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->gl_stack->setCurrentIndex (0);
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);
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 ()
@ -84,7 +116,7 @@ D25View::cellviews_changed ()
void
D25View::layer_properties_changed (int)
{
mp_ui->d25_view->refresh_view ();
// .. nothing yet ..
}
void
@ -113,6 +145,164 @@ D25View::menu_activated (const std::string &symbol)
}
}
D25View *
D25View::open (lay::LayoutView *view)
{
D25View *d25_view = view->get_plugin<lay::D25View> ();
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 ()
{
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;
}
}
void
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, name);
}
}
void
D25View::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)
{
if (! mp_ui->d25_view->has_error ()) {
mp_ui->d25_view->entry (data, dbu, zstart, 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) {
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 ()
{
if (! mp_ui->d25_view->has_error ()) {
mp_ui->d25_view->finish ();
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<lay::D25ViewWidget::LayerInfo> &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);
mp_ui->d25_view->set_cam_elevation (-initial_elevation);
// 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);
}
}
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);
@ -123,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
@ -189,6 +380,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 ()
{
@ -198,18 +398,33 @@ 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);
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<std::string, tl::Variant> add_pars;
tl::Recipe::make (m_generator, add_pars);
}
END_PROTECTED
}
void
D25View::fit_button_clicked ()
{
@ -242,6 +457,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 ()
{

View File

@ -24,9 +24,11 @@
#define HDR_layD25View
#include <QDialog>
#include <QListWidgetItem>
#include "tlObject.h"
#include "layBrowser.h"
#include "layViewOp.h"
namespace Ui
{
@ -38,6 +40,14 @@ namespace lay
class LayoutView;
}
namespace db
{
class Region;
class Edges;
class EdgePairs;
struct LayerProperties;
}
namespace lay
{
@ -54,6 +64,17 @@ public:
virtual void deactivated ();
virtual void activated ();
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, 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:
void accept ();
void reject ();
@ -67,12 +88,23 @@ private slots:
void vscale_slider_changed (int value);
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;
tl::DeferredMethod<D25View> dm_rerun_macro;
tl::DeferredMethod<D25View> dm_fit;
std::string m_generator;
void cellviews_changed ();
void layer_properties_changed (int);
void rerun_macro ();
void fit ();
};
}

View File

@ -26,11 +26,16 @@
#include "layLayoutView.h"
#include "dbRecursiveShapeIterator.h"
#include "dbD25TechnologyComponent.h"
#include "dbEdgeProcessor.h"
#include "dbPolygonGenerators.h"
#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"
@ -200,7 +205,10 @@ D25ViewWidget::D25ViewWidget (QWidget *parent)
setFormat (format);
m_zmin = m_zmax = 0.0;
m_zset = false;
m_display_open = false;
mp_view = 0;
m_has_error = false;
reset_viewport ();
}
@ -477,6 +485,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 *)
{
@ -496,86 +513,22 @@ D25ViewWidget::aspect_ratio () const
return double (width ()) / double (height ());
}
bool
D25ViewWidget::attach_view (LayoutView *view)
void
D25ViewWidget::clear ()
{
mp_view = view;
m_layers.clear ();
m_vertex_chunks.clear ();
m_line_chunks.clear ();
bool any = prepare_view ();
reset ();
return any;
}
namespace {
class ZDataCache
{
public:
ZDataCache () { }
std::vector<db::D25LayerInfo> operator() (lay::LayoutView *view, int cv_index, int layer_index)
{
std::map<int, std::map<int, std::vector<db::D25LayerInfo> > >::const_iterator c = m_cache.find (cv_index);
if (c != m_cache.end ()) {
std::map<int, std::vector<db::D25LayerInfo> >::const_iterator l = c->second.find (layer_index);
if (l != c->second.end ()) {
return l->second;
} else {
return std::vector<db::D25LayerInfo> ();
}
}
std::map<int, std::vector<db::D25LayerInfo> > &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<const db::D25TechnologyComponent *> (tech->component_by_name ("d25"));
}
if (comp) {
std::multimap<db::LayerProperties, db::D25LayerInfo, db::LPLogicalLessFunc> 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<db::LayerProperties, db::D25LayerInfo, db::LPLogicalLessFunc>::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<int, std::vector<db::D25LayerInfo> >::const_iterator l = lcache.find (layer_index);
if (l != lcache.end ()) {
return l->second;
} else {
return std::vector<db::D25LayerInfo> ();
}
}
private:
std::map<int, std::map<int, std::vector<db::D25LayerInfo> > > m_cache;
};
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])
@ -586,130 +539,181 @@ static void color_to_gl (color_t color, GLfloat (&gl_color) [4])
gl_color[3] = 1.0f;
}
void
D25ViewWidget::lp_to_info (const lay::LayerPropertiesNode &lp, LayerInfo &info)
static void color_to_gl (const color_t *color, GLfloat (&gl_color) [4])
{
color_to_gl (lp.fill_color (true), info.color);
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.color [3] = 0.0f;
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.color [3] > 0.5) {
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);
}
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<db::D25LayerInfo> zinfo;
if (! lp->has_children ()) {
zinfo = zdata (mp_view, lp->cellview_index (), lp->layer_index ());
}
for (std::vector<db::D25LayerInfo>::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<db::D25LayerInfo> zinfo;
if (! lp->has_children ()) {
zinfo = zdata (mp_view, lp->cellview_index (), lp->layer_index ());
}
for (std::vector<db::D25LayerInfo>::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;
info.visible = true;
}
void
D25ViewWidget::refresh_view ()
D25ViewWidget::open_display (const color_t *frame_color, const color_t *fill_color, const db::LayerProperties *like, const std::string *name)
{
if (! mp_view) {
return;
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.has_name = (name != 0 || like != 0);
if (name) {
info.name = *name;
} else if (like) {
info.name = like->to_string ();
}
for (lay::LayerPropertiesConstIterator lp = mp_view->begin_layers (); ! lp.at_end (); ++lp) {
info.vertex_chunk = &m_vertex_chunks.back ();
info.line_chunk = &m_line_chunks.back ();
std::pair<size_t, size_t> key = std::make_pair (lp->cellview_index (), lp->layer_index ());
std::multimap<std::pair<size_t, size_t>, 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]);
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;
}
++l;
}
}
m_layers.push_back (info);
m_display_open = true;
}
void
D25ViewWidget::close_display ()
{
m_display_open = false;
}
void
D25ViewWidget::enter (const db::RecursiveShapeIterator *iter, double zstart, double zstop)
{
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));
}
LayerInfo &info = m_layers.back ();
// try to establish a default color from the region's origin if required
if (mp_view && info.fill_color [3] == 0.0 && info.frame_color [3] == 0.0) {
if (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, info);
if (! info.has_name) {
info.name = like.to_string ();
info.has_name = true;
}
break;
}
}
}
} else {
// sequential assignment
lay::color_t color = mp_view->get_palette ().luminous_color_by_index (m_layers.size ());
color_to_gl (color, info.fill_color);
}
}
}
refresh ();
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<db::OriginalLayerRegion *> (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<db::OriginalLayerEdges *> (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<db::OriginalLayerEdgePairs *> (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 ()
{
// .. nothing yet ..
}
void
D25ViewWidget::attach_view (LayoutView *view)
{
mp_view = view;
}
void
@ -793,31 +797,23 @@ D25ViewWidget::render_wall (D25ViewWidget::triangle_chunks_type &chunks, D25View
}
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)
D25ViewWidget::render_region (tl::AbsoluteProgress &progress, D25ViewWidget::triangle_chunks_type &chunks, D25ViewWidget::line_chunks_type &line_chunks, const db::Region &region, double dbu, const db::Box &clip_box, double zstart, double zstop)
{
std::vector<db::Polygon> 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 ());
for (db::Region::const_iterator p = region.begin (); !p.at_end (); ++p) {
poly_heap.clear ();
db::clip_poly (polygon, clip_box, poly_heap, false /*keep holes*/);
db::clip_poly (*p, clip_box, poly_heap, false /*keep holes*/);
for (std::vector<db::Polygon>::const_iterator p = poly_heap.begin (); p != poly_heap.end (); ++p) {
++progress;
render_polygon (chunks, line_chunks, *p, layout.dbu (), zstart, zstop);
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, layout.dbu (), zstart, zstop);
render_wall (chunks, line_chunks, *e, dbu, zstart, zstop);
}
}
@ -825,6 +821,43 @@ D25ViewWidget::render_layout (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<bool, db::Edge> 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<bool, db::Edge> 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<double, double> find_grid (double v)
{
for (int p = -12; p < 12; ++p) {
@ -852,22 +885,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;
@ -1098,8 +1131,8 @@ D25ViewWidget::paintGL ()
glEnableVertexAttribArray (positions);
for (std::vector<LayerInfo>::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);
}
}

View File

@ -27,6 +27,7 @@
#include "layD25MemChunks.h"
#include "layD25Camera.h"
#include "layViewOp.h"
#include <QOpenGLWidget>
#include <QOpenGLVertexArrayObject>
@ -41,8 +42,11 @@
namespace db
{
class Layout;
class Cell;
class Region;
class Edges;
class EdgePairs;
class RecursiveShapeIterator;
struct LayerProperties;
}
namespace tl
@ -78,6 +82,20 @@ class D25ViewWidget
Q_OBJECT
public:
typedef lay::mem_chunks<GLfloat, 1024 * 18> triangle_chunks_type;
typedef lay::mem_chunks<GLfloat, 1024 * 6> 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;
std::string name;
bool has_name;
};
D25ViewWidget (QWidget *parent);
~D25ViewWidget ();
@ -88,8 +106,7 @@ public:
void mouseReleaseEvent (QMouseEvent *event);
void mouseMoveEvent (QMouseEvent *event);
bool attach_view(lay::LayoutView *view);
void refresh_view ();
void attach_view(lay::LayoutView *view);
QVector3D hit_point_with_scene(const QVector3D &line_dir);
void refresh ();
@ -129,6 +146,26 @@ public:
return m_error;
}
bool has_error () const
{
return m_has_error;
}
const std::vector<LayerInfo> &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 ();
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:
void scale_factor_changed (double f);
void vscale_factor_changed (double f);
@ -143,44 +180,36 @@ public slots:
void fit ();
private:
typedef lay::mem_chunks<GLfloat, 1024 * 18> triangle_chunks_type;
typedef lay::mem_chunks<GLfloat, 1024 * 6> line_chunks_type;
std::unique_ptr<D25InteractionMode> 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;
lay::LayoutView *mp_view;
db::DBox m_bbox;
double m_zmin, m_zmax;
bool m_zset;
bool m_display_open;
std::list<triangle_chunks_type> m_vertex_chunks;
std::list<line_chunks_type> 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<LayerInfo> m_layers;
std::multimap<std::pair<size_t, size_t>, 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);
void render_region (tl::AbsoluteProgress &progress, D25ViewWidget::triangle_chunks_type &vertex_chunks, D25ViewWidget::line_chunks_type &line_chunks, const db::Region &region, 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 &region, 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 &region, 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);
void enter (const db::RecursiveShapeIterator *iter, double zstart, double zstop);
};
}

View File

@ -4,9 +4,13 @@ DESTDIR = $$OUT_PWD/../../../../lay_plugins
include($$PWD/../../../lay_plugin.pri)
INCLUDEPATH += $$RDB_INC $$ANT_INC
DEPENDPATH += $$RDB_INC $$ANT_INC
INCLUDEPATH += $$RDB_INC $$ANT_INC $$QTBASIC_INC
DEPENDPATH += $$RDB_INC $$ANT_INC $$QTBASIC_INC
LIBS += -L$$DESTDIR/.. -lklayout_rdb -lklayout_ant
equals(HAVE_QTBINDINGS, "1") {
LIBS += -lklayout_qtbasic -lklayout_QtGui -lklayout_QtCore
}
HEADERS = \
layD25View.h \
@ -16,6 +20,7 @@ HEADERS = \
layD25Camera.h
SOURCES = \
gsiDeclLayD25View.cc \
layD25View.cc \
layD25ViewWidget.cc \
layD25Plugin.cc \
@ -26,6 +31,9 @@ SOURCES = \
FORMS = \
D25View.ui \
RESOURCES = \
layD25Resources.qrc \
greaterThan(QT_MAJOR_VERSION, 5) {
QT += openglwidgets
}

View File

@ -0,0 +1,63 @@
<?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.
#
# 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 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
# colors itself using the application's palette.
#
# zz(options) { block }
# zz(z(...), z(...), ..., options):
#
# 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)
zz(like: 7/0, name: "Metal") 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.