Merge branch 'master' of github.com:KLayout/klayout

This commit is contained in:
Matthias Koefferlein 2024-04-30 17:42:41 +02:00
commit e3f776a74e
99 changed files with 7489 additions and 544 deletions

View File

@ -2198,15 +2198,29 @@ Service::paste ()
}
}
std::vector<const db::DUserObject *> new_objects;
for (db::Clipboard::iterator c = db::Clipboard::instance ().begin (); c != db::Clipboard::instance ().end (); ++c) {
const db::ClipboardValue<ant::Object> *value = dynamic_cast<const db::ClipboardValue<ant::Object> *> (*c);
if (value) {
ant::Object *ruler = new ant::Object (value->get ());
ruler->id (++idmax);
mp_view->annotation_shapes ().insert (db::DUserObject (ruler));
new_objects.push_back (&mp_view->annotation_shapes ().insert (db::DUserObject (ruler)));
}
}
// make new objects selected
if (! new_objects.empty ()) {
for (auto r = new_objects.begin (); r != new_objects.end (); ++r) {
m_selected.insert (std::make_pair (mp_view->annotation_shapes ().iterator_from_pointer (*r), 0));
}
selection_to_view ();
}
}
}

View File

@ -288,6 +288,7 @@ VariantsCollectorBase::separate_variants (std::map<db::cell_index_type, std::map
if (! var_table) {
var_table = &var_table_intern;
}
tl_assert (var_table->empty ());
for (db::Layout::bottom_up_const_iterator c = mp_layout->begin_bottom_up (); c != mp_layout->end_bottom_up (); ++c) {
@ -311,6 +312,9 @@ VariantsCollectorBase::separate_variants (std::map<db::cell_index_type, std::map
cell.clear_insts ();
bool original_cell_is_variant = false;
db::ICplxTrans original_cell_variant;
int index = 0;
for (auto v = vc->second.begin (); v != vc->second.end (); ++v, ++index) {
@ -333,6 +337,8 @@ VariantsCollectorBase::separate_variants (std::map<db::cell_index_type, std::map
} else {
ci_var = *c;
original_cell_is_variant = true;
original_cell_variant = *v;
}
vt.insert (std::make_pair (*v, ci_var));
@ -341,12 +347,14 @@ VariantsCollectorBase::separate_variants (std::map<db::cell_index_type, std::map
}
// correct the first (remaining) entry
if (! vt.begin ()->first.is_unity ()) {
std::set<db::ICplxTrans> &tv = m_variants [*c];
tv.clear ();
tv.insert (vt.begin ()->first);
} else {
m_variants.erase (*c);
if (original_cell_is_variant) {
if (! original_cell_variant.is_unity ()) {
std::set<db::ICplxTrans> &tv = m_variants [*c];
tv.clear ();
tv.insert (original_cell_variant);
} else {
m_variants.erase (*c);
}
}
} else {

View File

@ -644,6 +644,11 @@ void Circuit::set_pin_ref_for_pin (size_t pin_id, Net::pin_iterator iter)
m_pin_refs [pin_id] = iter;
}
bool Circuit::is_empty () const
{
return m_nets.empty () && m_pins.empty () && m_devices.empty () && m_subcircuits.empty ();
}
void Circuit::blank ()
{
tl_assert (netlist () != 0);

View File

@ -759,6 +759,11 @@ public:
*/
void blank ();
/**
* @brief Gets a value indicating whether the circuit is empty
*/
bool is_empty () const;
/**
* @brief Generate memory statistics
*/

View File

@ -298,28 +298,57 @@ struct DeepShapeStore::LayoutHolder
{
public:
VariantsCreatedListener (DeepShapeStore::LayoutHolder *lh, db::Layout *ly)
: mp_lh (lh)
: mp_lh (lh), m_dbu (ly->dbu ())
{
ly->variants_created_event.add (this, &VariantsCreatedListener::variants_created);
}
private:
std::string var_desc (const db::ICplxTrans &t)
{
std::string s;
if (t.is_mirror ()) {
s += "m";
s += tl::to_string (t.angle () * 0.5);
} else {
s += "r";
s += tl::to_string (t.angle ());
}
if (t.is_mag ()) {
s += tl::sprintf ("*%.9g", t.mag ());
}
if (t.disp () != db::Vector ()) {
s += tl::sprintf ("(%.12g,%.12g)", t.disp ().x () * m_dbu, t.disp ().y () * m_dbu);
}
return s;
}
void variants_created (const std::map<db::cell_index_type, std::map<db::ICplxTrans, db::cell_index_type> > *var_map)
{
for (std::map<db::cell_index_type, std::map<db::ICplxTrans, db::cell_index_type> >::const_iterator i = var_map->begin (); i != var_map->end (); ++i) {
for (std::map<db::ICplxTrans, db::cell_index_type>::const_iterator j = i->second.begin (); j != i->second.end (); ++j) {
mp_lh->builder.register_variant (i->first, j->second);
if (i->first != j->second) {
mp_lh->builder.register_variant (i->first, j->second, var_desc (j->first));
}
}
// NOTE: variant conversion events are registered after variant formation events, so we can
// base the formed variants (first pass) on the originals.
for (std::map<db::ICplxTrans, db::cell_index_type>::const_iterator j = i->second.begin (); j != i->second.end (); ++j) {
if (i->first == j->second) {
mp_lh->builder.register_variant (i->first, j->second, var_desc (j->first));
}
}
}
}
DeepShapeStore::LayoutHolder *mp_lh;
double m_dbu;
};
LayoutHolder (const db::ICplxTrans &trans)
: refs (0), layout (false), builder (&layout, trans), variants_created (this, &layout)
{
// .. nothing yet ..
layout.set_hierarchy_builder (&builder);
}
void add_layer_ref (unsigned int layer)

View File

@ -98,13 +98,13 @@ bool EdgePairFilterByArea::selected (const db::EdgePair &edge_pair) const
// EdgePairFilterByArea implementation
InternalAngleEdgePairFilter::InternalAngleEdgePairFilter (double a, bool inverted)
: m_inverted (inverted), m_checker (a, true, a, true)
: m_checker (a, true, a, true, inverted, false)
{
// .. nothing yet ..
}
InternalAngleEdgePairFilter::InternalAngleEdgePairFilter (double amin, bool include_amin, double amax, bool include_amax, bool inverted)
: m_inverted (inverted), m_checker (amin, include_amin, amax, include_amax)
: m_checker (amin, include_amin, amax, include_amax, inverted, false)
{
// .. nothing yet ..
}
@ -122,7 +122,7 @@ InternalAngleEdgePairFilter::selected (const db::EdgePair &edge_pair) const
std::swap (d1, d2);
}
return m_checker (d1, d2) != m_inverted;
return m_checker (d1, d2);
}
}

View File

@ -215,8 +215,13 @@ EdgeSegmentSelector::process (const db::Edge &edge, std::vector<db::Edge> &res)
// -------------------------------------------------------------------------------------------------------------
// EdgeAngleChecker implementation
EdgeAngleChecker::EdgeAngleChecker (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end)
EdgeAngleChecker::EdgeAngleChecker (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end, bool inverse, bool absolute)
{
if (absolute && angle_start < -db::epsilon) {
angle_start = 0.0;
include_angle_start = true;
}
m_t_start = db::CplxTrans(1.0, angle_start, false, db::DVector ());
m_t_end = db::CplxTrans(1.0, angle_end, false, db::DVector ());
@ -225,6 +230,9 @@ EdgeAngleChecker::EdgeAngleChecker (double angle_start, bool include_angle_start
m_big_angle = (angle_end - angle_start + db::epsilon) > 180.0;
m_all = (angle_end - angle_start - db::epsilon) > 360.0;
m_absolute = absolute;
m_inverse = inverse;
}
bool
@ -255,14 +263,14 @@ EdgeAngleChecker::check (const db::Vector &a, const db::Vector &b) const
// -------------------------------------------------------------------------------------------------------------
// EdgeOrientationFilter implementation
EdgeOrientationFilter::EdgeOrientationFilter (double amin, bool include_amin, double amax, bool include_amax, bool inverse)
: m_inverse (inverse), m_checker (amin, include_amin, amax, include_amax)
EdgeOrientationFilter::EdgeOrientationFilter (double amin, bool include_amin, double amax, bool include_amax, bool inverse, bool absolute)
: m_checker (amin, include_amin, amax, include_amax, inverse, absolute)
{
// .. nothing yet ..
}
EdgeOrientationFilter::EdgeOrientationFilter (double a, bool inverse)
: m_inverse (inverse), m_checker (a, true, a, true)
EdgeOrientationFilter::EdgeOrientationFilter (double a, bool inverse, bool absolute)
: m_checker (a, true, a, true, inverse, absolute)
{
// .. nothing yet ..
}
@ -273,9 +281,9 @@ EdgeOrientationFilter::selected (const db::Edge &edge) const
// NOTE: this edge normalization confines the angle to a range between (-90 .. 90] (-90 excluded).
// A horizontal edge has 0 degree, a vertical one has 90 degree.
if (edge.dx () < 0 || (edge.dx () == 0 && edge.dy () < 0)) {
return m_checker (db::Vector (edge.ortho_length (), 0), -edge.d ()) != m_inverse;
return m_checker (db::Vector (edge.ortho_length (), 0), -edge.d ());
} else {
return m_checker (db::Vector (edge.ortho_length (), 0), edge.d ()) != m_inverse;
return m_checker (db::Vector (edge.ortho_length (), 0), edge.d ());
}
}
@ -289,20 +297,20 @@ SpecialEdgeOrientationFilter::SpecialEdgeOrientationFilter (FilterType type, boo
}
static EdgeAngleChecker s_ortho_checkers [] = {
EdgeAngleChecker (0.0, true, 0.0, true),
EdgeAngleChecker (90.0, true, 90.0, true)
EdgeAngleChecker (0.0, true, 0.0, true, false, false),
EdgeAngleChecker (90.0, true, 90.0, true, false, false)
};
static EdgeAngleChecker s_diagonal_checkers [] = {
EdgeAngleChecker (-45.0, true, -45.0, true),
EdgeAngleChecker (45.0, true, 45.0, true)
EdgeAngleChecker (-45.0, true, -45.0, true, false, false),
EdgeAngleChecker (45.0, true, 45.0, true, false, false)
};
static EdgeAngleChecker s_orthodiagonal_checkers [] = {
EdgeAngleChecker (-45.0, true, -45.0, true),
EdgeAngleChecker (0.0, true, 0.0, true),
EdgeAngleChecker (45.0, true, 45.0, true),
EdgeAngleChecker (90.0, true, 90.0, true)
EdgeAngleChecker (-45.0, true, -45.0, true, false, false),
EdgeAngleChecker (0.0, true, 0.0, true, false, false),
EdgeAngleChecker (45.0, true, 45.0, true, false, false),
EdgeAngleChecker (90.0, true, 90.0, true, false, false)
};
bool

View File

@ -142,22 +142,23 @@ private:
class DB_PUBLIC EdgeAngleChecker
{
public:
EdgeAngleChecker (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end);
EdgeAngleChecker (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end, bool inverse, bool absolute);
bool operator() (const db::Edge &a, const db::Edge &b) const
{
return m_all || check (a.d (), b.d ());
return (m_all || check (a.d (), b.d ()) || (m_absolute && check (b.d (), a.d ()))) != m_inverse;
}
bool operator() (const db::Vector &a, const db::Vector &b) const
{
return m_all || check (a, b);
return (m_all || check (a, b) || (m_absolute && check (b, a))) != m_inverse;
}
private:
db::CplxTrans m_t_start, m_t_end;
bool m_include_start, m_include_end;
bool m_big_angle, m_all;
bool m_inverse, m_absolute;
bool check (const db::Vector &a, const db::Vector &b) const;
};
@ -181,22 +182,24 @@ struct DB_PUBLIC EdgeOrientationFilter
* @param amin The minimum angle (measured against the x axis)
* @param amax The maximum angle (measured against the x axis)
* @param inverse If set to true, only edges not matching this criterion will be filtered
* @param absolute Angles are always positive
*
* This filter will filter out all edges whose angle against x axis
* is larger or equal to amin and less than amax.
*/
EdgeOrientationFilter (double amin, bool include_amin, double amax, bool include_amax, bool inverse);
EdgeOrientationFilter (double amin, bool include_amin, double amax, bool include_amax, bool inverse, bool absolute);
/**
* @brief Constructor
*
* @param a The angle (measured against the x axis)
* @param inverse If set to true, only edges not matching this criterion will be filtered
* @param absolute Angles are always positive
*
* This filter will filter out all edges whose angle against x axis
* is equal to a.
*/
EdgeOrientationFilter (double a, bool inverse);
EdgeOrientationFilter (double a, bool inverse, bool absolute);
/**
* @brief Returns true if the edge orientation matches the criterion

View File

@ -1216,11 +1216,13 @@ struct cluster_building_receiver
db::Connectivity::global_nets_iterator ge = mp_conn->end_global_connections (p.first);
for (db::Connectivity::global_nets_iterator g = mp_conn->begin_global_connections (p.first); g != ge; ++g) {
bool soft = (g->second != 0);
typename std::map<size_t, typename std::list<cluster_value>::iterator>::iterator icg = m_global_to_clusters.find (g->first);
if (icg == m_global_to_clusters.end ()) {
if (g->second != 0) {
if (soft) {
// soft connection to a new global cluster
m_clusters.push_back (cluster_value ());
@ -1240,7 +1242,7 @@ struct cluster_building_receiver
} else if (ic->second != icg->second) {
if (g->second != 0) {
if (soft) {
register_soft_connection (ic->second, icg->second, g->second);

View File

@ -513,6 +513,7 @@ public:
typedef db::box_tree<box_type, local_cluster<T>, local_cluster_box_convert<T> > tree_type;
typedef typename tree_type::touching_iterator touching_iterator;
typedef typename tree_type::const_iterator const_iterator;
typedef typename tree_type::iterator iterator;
/**
* @brief Creates an empty collection
@ -566,6 +567,22 @@ public:
return m_clusters.end ();
}
/**
* @brief Gets the clusters (begin iterator)
*/
iterator begin ()
{
return m_clusters.begin ();
}
/**
* @brief Gets the clusters (end iterator)
*/
iterator end ()
{
return m_clusters.end ();
}
/**
* @brief Gets a value indicating whether the cluster set is empty
*/

View File

@ -182,14 +182,24 @@ HierarchyBuilder::reset ()
m_cells_to_be_filled.clear ();
m_cell_map.clear ();
m_variants_of_sources_map.clear ();
m_cells_seen.clear ();
m_cell_stack.clear ();
m_cm_entry = null_iterator;
m_cm_new_entry = false;
}
const std::pair<db::cell_index_type, std::string> &
HierarchyBuilder::variant_of_source (db::cell_index_type target) const
{
static std::pair<db::cell_index_type, std::string> def (std::numeric_limits<db::cell_index_type>::max (), std::string ());
auto vs = m_variants_of_sources_map.find (target);
return vs != m_variants_of_sources_map.end () ? vs->second : def;
}
void
HierarchyBuilder::register_variant (db::cell_index_type non_var, db::cell_index_type var)
HierarchyBuilder::register_variant (db::cell_index_type non_var, db::cell_index_type var, const std::string &description)
{
// non_var (despite its name) may be a variant created previously.
variant_to_original_target_map_type::const_iterator v = m_variants_to_original_target_map.find (non_var);
@ -199,11 +209,23 @@ HierarchyBuilder::register_variant (db::cell_index_type non_var, db::cell_index_
m_original_targets_to_variants_map [non_var].push_back (var);
m_variants_to_original_target_map.insert (std::make_pair (var, non_var));
auto vs = m_variants_of_sources_map.find (non_var);
if (vs != m_variants_of_sources_map.end ()) {
std::string new_description = vs->second.second;
if (! new_description.empty ()) {
new_description += ";";
}
new_description += description;
m_variants_of_sources_map [var] = std::make_pair (vs->second.first, new_description);
}
}
void
HierarchyBuilder::unregister_variant (db::cell_index_type var)
{
m_variants_of_sources_map.erase (var);
variant_to_original_target_map_type::iterator v = m_variants_to_original_target_map.find (var);
if (v == m_variants_to_original_target_map.end ()) {
return;
@ -257,6 +279,7 @@ HierarchyBuilder::begin (const RecursiveShapeIterator *iter)
if (m_cm_entry == m_cell_map.end ()) {
db::cell_index_type new_top_index = mp_target->add_cell (iter->layout ()->cell_name (key.original_cell));
m_cm_entry = m_cell_map.insert (std::make_pair (key, new_top_index)).first;
m_variants_of_sources_map.insert (std::make_pair (new_top_index, std::make_pair (key.original_cell, std::string ())));
}
db::Cell &new_top = mp_target->cell (m_cm_entry->second);
@ -324,17 +347,34 @@ HierarchyBuilder::make_cell_variant (const HierarchyBuilder::CellMapKey &key, co
if (m_cm_entry == m_cell_map.end ()) {
std::string cn = cell_name;
std::string description;
if (! key.clip_region.empty ()) {
cn += "$CLIP_VAR";
description += "CLIP";
}
if (key.inactive) {
cn += "$DIS";
if (! description.empty ()) {
description += "/";
}
description += "DISABLED";
}
new_cell = mp_target->add_cell (cn.c_str ());
std::string new_name = mp_target->cell_name (new_cell);
if (new_name.size () > cn.size ()) {
// use cell name extension to uniquify the description
description += new_name.c_str () + cn.size ();
}
m_cm_entry = m_cell_map.insert (std::make_pair (key, new_cell)).first;
m_cm_new_entry = true;
m_cells_to_be_filled.insert (new_cell);
m_variants_of_sources_map.insert (std::make_pair (new_cell, std::make_pair (key.original_cell, description)));
} else {
new_cell = m_cm_entry->second;
}

View File

@ -389,7 +389,7 @@ public:
* The first cell is either the original, non-variant target cell or itself a variant.
* The second cell will be registered as a variant of the first one.
*/
void register_variant (db::cell_index_type non_var, db::cell_index_type var);
void register_variant (db::cell_index_type non_var, db::cell_index_type var, const std::string &description);
/**
* @brief Unregisters a cell as a variant
@ -404,8 +404,23 @@ public:
return m_variants_to_original_target_map.find (ci) != m_variants_to_original_target_map.end ();
}
/**
* @brief Gets the information about the target/source + variant relationship
*
* For a target cell, returns a pointer to a pair with the cell index of the source cell and
* a string describing the variant this cell forms of the source cell.
*
* The description string is empty if the cell is not a variant.
*/
const std::pair<db::cell_index_type, std::string> &variant_of_source (db::cell_index_type target) const;
/**
* @brief Gets the original target for a variant cell
*
* The original cell is the first-generation target-space cell that variants have been created for.
* This still can mean that the original cell is a clip variant of a source cell.
*
* Target-to-source variants can be derived with "variant_of_source".
*/
db::cell_index_type original_target_for_variant (db::cell_index_type ci) const;
@ -419,6 +434,7 @@ private:
cell_map_type m_cell_map;
original_target_to_variants_map_type m_original_targets_to_variants_map;
variant_to_original_target_map_type m_variants_to_original_target_map;
std::map<db::cell_index_type, std::pair<db::cell_index_type, std::string> > m_variants_of_sources_map;
std::set<cell_map_type::key_type> m_cells_seen;
std::set<db::cell_index_type> m_cells_to_be_filled;

View File

@ -439,6 +439,7 @@ LayoutOrCellContextInfo::has_meta_info () const
Layout::Layout (db::Manager *manager)
: db::Object (manager),
mp_library (0),
mp_builder (0),
m_cells_size (0),
m_invalid (0),
m_top_cells (0),
@ -454,6 +455,7 @@ Layout::Layout (db::Manager *manager)
Layout::Layout (bool editable, db::Manager *manager)
: db::Object (manager),
mp_library (0),
mp_builder (0),
m_cells_size (0),
m_invalid (0),
m_top_cells (0),
@ -473,6 +475,7 @@ Layout::Layout (const db::Layout &layout)
tl::Object (),
tl::UniqueId (),
mp_library (0),
mp_builder (0),
m_cells_size (0),
m_invalid (0),
m_top_cells (0),

View File

@ -72,6 +72,7 @@ class Technology;
class CellMapping;
class LayerMapping;
class VariantsCollectorBase;
class HierarchyBuilder;
template <class Coord> class generic_repository;
typedef generic_repository<db::Coord> GenericRepository;
@ -2113,6 +2114,24 @@ public:
*/
const MetaInfo &meta_info (db::cell_index_type ci, meta_info_name_id_type name_id) const;
/**
* @brief Sets the hierarchy builder reference
* Used internally
*/
void set_hierarchy_builder (db::HierarchyBuilder *builder)
{
mp_builder = builder;
}
/**
* @brief Gets the hierarchy builder
* Used internally
*/
db::HierarchyBuilder *builder () const
{
return mp_builder;
}
/**
* @brief This event is triggered when the technology changes
*/
@ -2138,6 +2157,7 @@ protected:
private:
db::Library *mp_library;
db::HierarchyBuilder *mp_builder;
cell_list m_cells;
size_t m_cells_size;
cell_ptr_vector m_cell_ptrs;

View File

@ -395,12 +395,18 @@ void LayoutToNetlistStandardReader::read_netlist (db::Netlist *netlist, db::Layo
throw tl::Exception (tl::to_string (tr ("Duplicate definition of device class: ")) + class_name);
}
db::DeviceClassTemplateBase *dct = db::DeviceClassTemplateBase::template_by_name (templ_name);
if (! dct) {
throw tl::Exception (tl::to_string (tr ("Invalid device class template: ")) + templ_name);
db::DeviceClass *dc;
if (templ_name.empty ()) {
// generic device class (issue #1696)
dc = new db::DeviceClass ();
} else {
db::DeviceClassTemplateBase *dct = db::DeviceClassTemplateBase::template_by_name (templ_name);
if (! dct) {
throw tl::Exception (tl::to_string (tr ("Invalid device class template: ")) + templ_name);
}
dc = dct->create ();
}
db::DeviceClass *dc = dct->create ();
dc->set_name (class_name);
netlist->add_device_class (dc);

View File

@ -228,8 +228,8 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo
const db::Cell &cell = mp_layout->cell (*cid);
const connected_clusters_type &clusters = mp_clusters->clusters_per_cell (*cid);
if (clusters.empty ()) {
connected_clusters_type &per_cell_clusters = mp_clusters->clusters_per_cell (*cid);
if (per_cell_clusters.empty ()) {
bool any_good = false;
@ -251,7 +251,7 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo
if (dm) {
// This is a device abstract cell:
// make the terminal to cluster ID connections for the device abstract from the device cells
make_device_abstract_connections (dm, clusters);
make_device_abstract_connections (dm, per_cell_clusters);
continue;
}
@ -284,12 +284,12 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo
}
for (connected_clusters_type::all_iterator c = clusters.begin_all (); ! c.at_end (); ++c) {
for (connected_clusters_type::all_iterator c = per_cell_clusters.begin_all (); ! c.at_end (); ++c) {
const db::local_cluster<db::NetShape> &lc = clusters.cluster_by_id (*c);
const connected_clusters_type::connections_type &cc = clusters.connections_for_cluster (*c);
const std::set<size_t> &sc_up = clusters.upward_soft_connections (*c);
const std::set<size_t> &sc_down = clusters.downward_soft_connections (*c);
const db::local_cluster<db::NetShape> &lc = per_cell_clusters.cluster_by_id (*c);
const connected_clusters_type::connections_type &cc = per_cell_clusters.connections_for_cluster (*c);
const std::set<size_t> &sc_up = per_cell_clusters.upward_soft_connections (*c);
const std::set<size_t> &sc_down = per_cell_clusters.downward_soft_connections (*c);
if (cc.empty () && sc_up.empty () && sc_down.empty () && lc.empty ()) {
// this is an entirely empty cluster so we skip it.
// Such clusters are left over when joining clusters.
@ -301,14 +301,14 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo
circuit->add_net (net);
// make subcircuit connections (also make the subcircuits if required) from the connections of the clusters
make_and_connect_subcircuits (circuit, clusters, *c, net, subcircuits, circuits, pins_per_cluster_per_cell);
make_and_connect_subcircuits (circuit, per_cell_clusters, *c, net, subcircuits, circuits, pins_per_cluster_per_cell);
// connect devices
connect_devices (circuit, clusters, *c, net);
connect_devices (circuit, per_cell_clusters, *c, net);
// collect labels to net names
std::set<std::string> net_names;
collect_labels (clusters, *c, net_names);
collect_labels (per_cell_clusters, *c, net_names);
// add the global names as second priority
if (net_names.empty ()) {
@ -323,7 +323,7 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo
// made to satisfy the subcircuit's pin, but not to make a physical connection.
// Don't know whether this is a good idea, so this code is disabled for now.
if (net_names.empty () && clusters.is_dummy (*c) && net->subcircuit_pin_count () == 1) {
if (net_names.empty () && per_cell_clusters.is_dummy (*c) && net->subcircuit_pin_count () == 1) {
// in the case of a dummy connection (partially connected subcircuits) create a
// new name indicating the subcircuit and the subcircuit net name - this makes subcircuit
// net names available (the net is pseudo-root inside in the subcircuit)
@ -337,7 +337,7 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo
assign_net_names (net, net_names);
if (! clusters.is_root (*c)) {
if (! per_cell_clusters.is_root (*c)) {
// a non-root cluster makes a pin
size_t pin_id = make_pin (circuit, net);
c2p.insert (std::make_pair (*c, pin_id));
@ -364,28 +364,73 @@ NetlistExtractor::assign_net_names (db::Net *net, const std::set<std::string> &n
net->set_name (nn);
}
static void
collect_soft_connected_clusters (size_t from_id, const NetlistExtractor::connected_clusters_type &clusters, std::set<size_t> &ids)
{
if (ids.find (from_id) != ids.end ()) {
return;
}
ids.insert (from_id);
auto upward = clusters.upward_soft_connections (from_id);
for (auto i = upward.begin (); i != upward.end (); ++i) {
collect_soft_connected_clusters (*i, clusters, ids);
}
auto downward = clusters.downward_soft_connections (from_id);
for (auto i = downward.begin (); i != downward.end (); ++i) {
collect_soft_connected_clusters (*i, clusters, ids);
}
}
void
NetlistExtractor::make_device_abstract_connections (db::DeviceAbstract *dm, const connected_clusters_type &clusters)
NetlistExtractor::make_device_abstract_connections (db::DeviceAbstract *dm, connected_clusters_type &clusters)
{
// make the terminal to cluster ID connections for the device abstract from the device cells
if (m_terminal_annot_name_id.first) {
for (connected_clusters_type::const_iterator dc = clusters.begin (); dc != clusters.end (); ++dc) {
for (connected_clusters_type::iterator dc = clusters.begin (); dc != clusters.end (); ++dc) {
for (local_cluster_type::attr_iterator a = dc->begin_attr (); a != dc->end_attr (); ++a) {
std::set<size_t> ids;
collect_soft_connected_clusters (dc->id (), clusters, ids);
for (auto id = ids.begin (); id != ids.end (); ++id) {
const local_cluster_type &lc = clusters.cluster_by_id (*id);
bool join = false;
for (local_cluster_type::attr_iterator a = lc.begin_attr (); a != lc.end_attr (); ++a) {
if (! db::is_prop_id_attr (*a)) {
continue;
}
db::properties_id_type pi = db::prop_id_from_attr (*a);
const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (pi);
for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) {
if (j->first == m_terminal_annot_name_id.second) {
size_t terminal_id = j->second.to<size_t> ();
if (*id != dc->id ()) {
tl::warn << tl::sprintf (tl::to_string (tr ("Ignoring soft connection at device terminal %s for device %s")), dm->device_class ()->terminal_definition (terminal_id)->name (), dm->device_class ()->name ());
join = true;
}
dm->set_cluster_id_for_terminal (terminal_id, dc->id ());
}
}
if (! db::is_prop_id_attr (*a)) {
continue;
}
db::properties_id_type pi = db::prop_id_from_attr (*a);
const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (pi);
for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end (); ++j) {
if (j->first == m_terminal_annot_name_id.second) {
dm->set_cluster_id_for_terminal (j->second.to<size_t> (), dc->id ());
}
if (join) {
// copy the terminal attributes and shapes so we attach the terminal here in the device connection step
clusters.join_cluster_with (dc->id (), *id);
}
}

View File

@ -233,7 +233,7 @@ private:
/**
* @brief Makes the terminal to cluster ID connections of the device abstract
*/
void make_device_abstract_connections (db::DeviceAbstract *dm, const connected_clusters_type &clusters);
void make_device_abstract_connections (db::DeviceAbstract *dm, connected_clusters_type &clusters);
};

View File

@ -943,6 +943,12 @@ SpiceNetlistBuilder::circuit_for (const SpiceCachedCircuit *cc, const parameters
if (cp == c->second.end ()) {
return 0;
}
// a null pointer indicates that we are currently defining this circuit
if (cp->second == 0) {
error (tl::sprintf (tl::to_string (tr ("Subcircuit '%s' called recursively")), cc->name ()));
}
return cp->second;
}
@ -1055,7 +1061,8 @@ SpiceNetlistBuilder::build_circuit (const SpiceCachedCircuit *cc, const paramete
c->set_name (make_circuit_name (cc->name (), pv));
}
register_circuit_for (cc, pv, c, anonymous_top_level);
// pre-register the circuit - allows detecting recursive calls
register_circuit_for (cc, pv, 0, false);
std::unique_ptr<std::map<std::string, db::Net *> > n2n (mp_nets_by_name.release ());
mp_nets_by_name.reset (0);
@ -1095,6 +1102,14 @@ SpiceNetlistBuilder::build_circuit (const SpiceCachedCircuit *cc, const paramete
std::swap (c, mp_netlist_circuit);
std::swap (vars, m_variables);
// final registration if required
if (! anonymous_top_level || ! c->is_empty ()) {
register_circuit_for (cc, pv, c, anonymous_top_level);
} else {
mp_netlist->remove_circuit (c);
c = 0;
}
return c;
}

View File

@ -30,8 +30,8 @@ namespace db
// -----------------------------------------------------------------------------------
// CornerDetectorCore implementation
CornerDetectorCore::CornerDetectorCore (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end)
: m_checker (angle_start, include_angle_start, angle_end, include_angle_end)
CornerDetectorCore::CornerDetectorCore (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end, bool inverse, bool absolute)
: m_checker (angle_start, include_angle_start, angle_end, include_angle_end, inverse, absolute)
{
// .. nothing yet ..
}

View File

@ -114,7 +114,7 @@ private:
class DB_PUBLIC CornerDetectorCore
{
public:
CornerDetectorCore (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end);
CornerDetectorCore (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end, bool inverse, bool absolute);
virtual ~CornerDetectorCore () { }
void detect_corners (const db::Polygon &poly, const CornerPointDelivery &delivery) const;
@ -130,8 +130,8 @@ class DB_PUBLIC CornersAsRectangles
: public db::PolygonProcessorBase, private CornerDetectorCore
{
public:
CornersAsRectangles (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end, db::Coord dim = 1)
: CornerDetectorCore (angle_start, include_angle_start, angle_end, include_angle_end), m_dim (dim)
CornersAsRectangles (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end, bool inverse, bool absolute, db::Coord dim = 1)
: CornerDetectorCore (angle_start, include_angle_start, angle_end, include_angle_end, inverse, absolute), m_dim (dim)
{
// .. nothing yet ..
}
@ -159,8 +159,8 @@ class DB_PUBLIC CornersAsDots
: public db::PolygonToEdgeProcessorBase, private CornerDetectorCore
{
public:
CornersAsDots (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end)
: CornerDetectorCore (angle_start, include_angle_start, angle_end, include_angle_end)
CornersAsDots (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end, bool inverse, bool absolute)
: CornerDetectorCore (angle_start, include_angle_start, angle_end, include_angle_end, inverse, absolute)
{
// .. nothing yet ..
}
@ -184,8 +184,8 @@ class DB_PUBLIC CornersAsEdgePairs
: public db::PolygonToEdgePairProcessorBase, private CornerDetectorCore
{
public:
CornersAsEdgePairs (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end)
: CornerDetectorCore (angle_start, include_angle_start, angle_end, include_angle_end)
CornersAsEdgePairs (double angle_start, bool include_angle_start, double angle_end, bool include_angle_end, bool inverse, bool absolute)
: CornerDetectorCore (angle_start, include_angle_start, angle_end, include_angle_end, inverse, absolute)
{
// .. nothing yet ..
}

View File

@ -214,22 +214,22 @@ static db::CompoundRegionOperationNode *new_count_filter (db::CompoundRegionOper
return new db::CompoundRegionCountFilterNode (input, invert, min_count, max_count);
}
static db::CompoundRegionOperationNode *new_corners_as_rectangles (db::CompoundRegionOperationNode *input, double angle_start, bool include_angle_start, double angle_end, bool include_angle_end, db::Coord dim = 1)
static db::CompoundRegionOperationNode *new_corners_as_rectangles (db::CompoundRegionOperationNode *input, double angle_start, bool include_angle_start, double angle_end, bool include_angle_end, db::Coord dim, bool inverse, bool absolute)
{
check_non_null (input, "input");
return new db::CompoundRegionProcessingOperationNode (new db::CornersAsRectangles (angle_start, include_angle_start, angle_end, include_angle_end, dim), input, true /*processor is owned*/, dim /*dist adder*/);
return new db::CompoundRegionProcessingOperationNode (new db::CornersAsRectangles (angle_start, include_angle_start, angle_end, include_angle_end, inverse, absolute, dim), input, true /*processor is owned*/, dim /*dist adder*/);
}
static db::CompoundRegionOperationNode *new_corners_as_dots (db::CompoundRegionOperationNode *input, double angle_start, bool include_angle_start, double angle_end, bool include_angle_end)
static db::CompoundRegionOperationNode *new_corners_as_dots (db::CompoundRegionOperationNode *input, double angle_start, bool include_angle_start, double angle_end, bool include_angle_end, bool inverse, bool absolute)
{
check_non_null (input, "input");
return new db::CompoundRegionToEdgeProcessingOperationNode (new db::CornersAsDots (angle_start, include_angle_start, angle_end, include_angle_end), input, true /*processor is owned*/);
return new db::CompoundRegionToEdgeProcessingOperationNode (new db::CornersAsDots (angle_start, include_angle_start, angle_end, include_angle_end, inverse, absolute), input, true /*processor is owned*/);
}
static db::CompoundRegionOperationNode *new_corners_as_edge_pairs (db::CompoundRegionOperationNode *input, double angle_start, bool include_angle_start, double angle_end, bool include_angle_end)
static db::CompoundRegionOperationNode *new_corners_as_edge_pairs (db::CompoundRegionOperationNode *input, double angle_start, bool include_angle_start, double angle_end, bool include_angle_end, bool inverse, bool absolute)
{
check_non_null (input, "input");
return new db::CompoundRegionToEdgePairProcessingOperationNode (new db::CornersAsEdgePairs (angle_start, include_angle_start, angle_end, include_angle_end), input, true /*processor is owned*/);
return new db::CompoundRegionToEdgePairProcessingOperationNode (new db::CornersAsEdgePairs (angle_start, include_angle_start, angle_end, include_angle_end, inverse, absolute), input, true /*processor is owned*/);
}
static db::CompoundRegionOperationNode *new_extents (db::CompoundRegionOperationNode *input, db::Coord e)
@ -341,10 +341,10 @@ static db::CompoundRegionOperationNode *new_edge_length_sum_filter (db::Compound
return new db::CompoundRegionEdgeFilterOperationNode (new db::EdgeLengthFilter (lmin, lmax, inverse), input, true /*processor is owned*/, true /*sum*/);
}
static db::CompoundRegionOperationNode *new_edge_orientation_filter (db::CompoundRegionOperationNode *input, bool inverse, double amin, bool include_amin, double amax, bool include_amax)
static db::CompoundRegionOperationNode *new_edge_orientation_filter (db::CompoundRegionOperationNode *input, bool inverse, double amin, bool include_amin, double amax, bool include_amax, bool absolute_angle)
{
check_non_null (input, "input");
return new db::CompoundRegionEdgeFilterOperationNode (new db::EdgeOrientationFilter (amin, include_amin, amax, include_amax, inverse), input, true /*processor is owned*/);
return new db::CompoundRegionEdgeFilterOperationNode (new db::EdgeOrientationFilter (amin, include_amin, amax, include_amax, inverse, absolute_angle), input, true /*processor is owned*/);
}
static db::CompoundRegionOperationNode *new_polygons (db::CompoundRegionOperationNode *input, db::Coord e)
@ -617,17 +617,21 @@ Class<db::CompoundRegionOperationNode> decl_CompoundRegionOperationNode ("db", "
gsi::constructor ("new_count_filter", &new_count_filter, gsi::arg ("inputs"), gsi::arg ("invert", false), gsi::arg ("min_count", size_t (0)), gsi::arg ("max_count", std::numeric_limits<size_t>::max ()),
"@brief Creates a node selecting results but their shape count.\n"
) +
gsi::constructor ("new_corners_as_rectangles", &new_corners_as_rectangles, gsi::arg ("input"), gsi::arg ("angle_min"), gsi::arg ("include_angle_min"), gsi::arg ("angle_max"), gsi::arg ("include_angle_max"), gsi::arg ("dim"),
gsi::constructor ("new_corners_as_rectangles", &new_corners_as_rectangles, gsi::arg ("input"), gsi::arg ("angle_min"), gsi::arg ("include_angle_min"), gsi::arg ("angle_max"), gsi::arg ("include_angle_max"), gsi::arg ("dim"), gsi::arg ("inverse", false), gsi::arg ("absolute", false),
"@brief Creates a node turning corners into rectangles.\n"
"\n"
"'absolute' and 'inverse' arguments have been added in version 0.29.1.\n"
) +
gsi::constructor ("new_corners_as_dots", &new_corners_as_dots, gsi::arg ("input"), gsi::arg ("angle_min"), gsi::arg ("include_angle_min"), gsi::arg ("angle_max"), gsi::arg ("include_angle_max"),
gsi::constructor ("new_corners_as_dots", &new_corners_as_dots, gsi::arg ("input"), gsi::arg ("angle_min"), gsi::arg ("include_angle_min"), gsi::arg ("angle_max"), gsi::arg ("include_angle_max"), gsi::arg ("inverse", false), gsi::arg ("absolute", false),
"@brief Creates a node turning corners into dots (single-point edges).\n"
"\n"
"'absolute' and 'inverse' arguments have been added in version 0.29.1.\n"
) +
gsi::constructor ("new_corners_as_edge_pairs", &new_corners_as_edge_pairs, gsi::arg ("input"), gsi::arg ("angle_min"), gsi::arg ("include_angle_min"), gsi::arg ("angle_max"), gsi::arg ("include_angle_max"),
gsi::constructor ("new_corners_as_edge_pairs", &new_corners_as_edge_pairs, gsi::arg ("input"), gsi::arg ("angle_min"), gsi::arg ("include_angle_min"), gsi::arg ("angle_max"), gsi::arg ("include_angle_max"), gsi::arg ("inverse", false), gsi::arg ("absolute", false),
"@brief Creates a node turning corners into edge pairs containing the two edges adjacent to the corner.\n"
"The first edge will be the incoming edge and the second one the outgoing edge.\n"
"\n"
"This feature has been introduced in version 0.27.1.\n"
"This feature has been introduced in version 0.27.1. 'absolute' and 'inverse' arguments have been added in version 0.29.1.\n"
) +
gsi::constructor ("new_extents", &new_extents, gsi::arg ("input"), gsi::arg ("e", 0),
"@brief Creates a node returning the extents of the objects.\n"
@ -759,8 +763,10 @@ Class<db::CompoundRegionOperationNode> decl_CompoundRegionOperationNode ("db", "
gsi::constructor ("new_edge_length_sum_filter", &new_edge_length_sum_filter, gsi::arg ("input"), gsi::arg ("inverse", false), gsi::arg ("lmin", 0), gsi::arg ("lmax", std::numeric_limits<db::Edge::distance_type>::max (), "max"),
"@brief Creates a node filtering edges by their length sum (over the local set).\n"
) +
gsi::constructor ("new_edge_orientation_filter", &new_edge_orientation_filter, gsi::arg ("input"), gsi::arg ("inverse"), gsi::arg ("amin"), gsi::arg ("include_amin"), gsi::arg ("amax"), gsi::arg ("include_amax"),
gsi::constructor ("new_edge_orientation_filter", &new_edge_orientation_filter, gsi::arg ("input"), gsi::arg ("inverse"), gsi::arg ("amin"), gsi::arg ("include_amin"), gsi::arg ("amax"), gsi::arg ("include_amax"), gsi::arg ("absolute_angle", false),
"@brief Creates a node filtering edges by their orientation.\n"
"\n"
"'absolute_angle' has been introduced in version 0.29.1."
) +
gsi::constructor ("new_polygons", &new_polygons, gsi::arg ("input"), gsi::arg ("e", 0),
"@brief Creates a node converting the input to polygons.\n"

View File

@ -432,14 +432,28 @@ static db::EdgePairs with_length_both2 (const db::EdgePairs *r, const tl::Varian
static db::EdgePairs with_angle1 (const db::EdgePairs *r, double a, bool inverse)
{
db::EdgeOrientationFilter f (a, inverse);
db::EdgeOrientationFilter f (a, inverse, false);
db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/);
return r->filtered (ef);
}
static db::EdgePairs with_angle2 (const db::EdgePairs *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax)
{
db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse);
db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse, false);
db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/);
return r->filtered (ef);
}
static db::EdgePairs with_abs_angle1 (const db::EdgePairs *r, double a, bool inverse)
{
db::EdgeOrientationFilter f (a, inverse, true);
db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/);
return r->filtered (ef);
}
static db::EdgePairs with_abs_angle2 (const db::EdgePairs *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax)
{
db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse, true);
db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/);
return r->filtered (ef);
}
@ -453,14 +467,28 @@ static db::EdgePairs with_angle3 (const db::EdgePairs *r, db::SpecialEdgeOrienta
static db::EdgePairs with_angle_both1 (const db::EdgePairs *r, double a, bool inverse)
{
db::EdgeOrientationFilter f (a, inverse);
db::EdgeOrientationFilter f (a, inverse, false);
db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/);
return r->filtered (ef);
}
static db::EdgePairs with_angle_both2 (const db::EdgePairs *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax)
{
db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse);
db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse, false);
db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/);
return r->filtered (ef);
}
static db::EdgePairs with_abs_angle_both1 (const db::EdgePairs *r, double a, bool inverse)
{
db::EdgeOrientationFilter f (a, inverse, true);
db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/);
return r->filtered (ef);
}
static db::EdgePairs with_abs_angle_both2 (const db::EdgePairs *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax)
{
db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse, true);
db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/);
return r->filtered (ef);
}
@ -967,6 +995,22 @@ Class<db::EdgePairs> decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs",
"\n"
"This method has been added in version 0.27.1.\n"
) +
method_ext ("with_abs_angle", with_abs_angle1, gsi::arg ("angle"), gsi::arg ("inverse"),
"@brief Filter the edge pairs by orientation of their edges\n"
"\n"
"This method behaves like \\with_angle, but angles are always positive - i.e. there is no "
"differentiation between edges sloping 'down' vs. edges sloping 'up.\n"
"\n"
"This method has been added in version 0.29.1.\n"
) +
method_ext ("with_abs_angle", with_abs_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false),
"@brief Filter the edge pairs by orientation of their edges\n"
"\n"
"This method behaves like \\with_angle, but angles are always positive - i.e. there is no "
"differentiation between edges sloping 'down' vs. edges sloping 'up.\n"
"\n"
"This method has been added in version 0.29.1.\n"
) +
method_ext ("with_angle", with_angle3, gsi::arg ("type"), gsi::arg ("inverse"),
"@brief Filter the edge pairs by orientation of their edges\n"
"Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only "
@ -1027,6 +1071,21 @@ Class<db::EdgePairs> decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs",
"\n"
"This method has been added in version 0.27.1.\n"
) +
method_ext ("with_abs_angle_both", with_abs_angle_both1, gsi::arg ("angle"), gsi::arg ("inverse"),
"@brief Filter the edge pairs by orientation of both of their edges\n"
"\n"
"This method behaves like \\with_angle_both, but angles are always positive - i.e. there is no "
"differentiation between edges sloping 'down' vs. edges sloping 'up.\n"
"\n"
"This method has been added in version 0.29.1.\n"
) +
method_ext ("with_abs_angle_both", with_abs_angle_both2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false),
"\n"
"This method behaves like \\with_angle_both, but angles are always positive - i.e. there is no "
"differentiation between edges sloping 'down' vs. edges sloping 'up.\n"
"\n"
"This method has been added in version 0.29.1.\n"
) +
method_ext ("with_angle_both", with_angle_both3, gsi::arg ("type"), gsi::arg ("inverse"),
"@brief Filter the edge pairs by orientation of their edges\n"
"Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only "

View File

@ -440,13 +440,25 @@ static db::Edges with_length2 (const db::Edges *r, const tl::Variant &min, const
static db::Edges with_angle1 (const db::Edges *r, double a, bool inverse)
{
db::EdgeOrientationFilter f (a, inverse);
db::EdgeOrientationFilter f (a, inverse, false);
return r->filtered (f);
}
static db::Edges with_angle2 (const db::Edges *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax)
{
db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse);
db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse, false);
return r->filtered (f);
}
static db::Edges with_abs_angle1 (const db::Edges *r, double a, bool inverse)
{
db::EdgeOrientationFilter f (a, inverse, true);
return r->filtered (f);
}
static db::Edges with_abs_angle2 (const db::Edges *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax)
{
db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse, true);
return r->filtered (f);
}
@ -923,7 +935,23 @@ Class<db::Edges> decl_Edges (decl_dbShapeCollection, "db", "Edges",
"With \"include_min_angle\" set to true (the default), the minimum angle is included in the criterion while with false, the "
"minimum angle itself is not included. Same for \"include_max_angle\" where the default is false, meaning the maximum angle is not included in the range.\n"
"\n"
"The two \"include..\" arguments have been added in version 0.27."
"The two \"include..\" arguments have been added in version 0.27.\n"
) +
method_ext ("with_abs_angle", with_abs_angle1, gsi::arg ("angle"), gsi::arg ("inverse"),
"@brief Filter the edges by orientation\n"
"\n"
"This method behaves like \\with_angle, but angles are always positive - i.e. there is no "
"differentiation between edges sloping 'down' vs. edges sloping 'up.\n"
"\n"
"This method has been added in version 0.29.1.\n"
) +
method_ext ("with_abs_angle", with_abs_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false),
"@brief Filter the edges by orientation\n"
"\n"
"This method behaves like \\with_angle, but angles are always positive - i.e. there is no "
"differentiation between edges sloping 'down' vs. edges sloping 'up.\n"
"\n"
"This method has been added in version 0.29.1.\n"
) +
method_ext ("with_angle", with_angle3, gsi::arg ("type"), gsi::arg ("inverse"),
"@brief Filters the edges by orientation type\n"

View File

@ -300,19 +300,19 @@ static db::Region *texts_as_boxes2 (const db::Region *r, db::DeepShapeStore &dss
return new db::Region (r->texts_as_boxes (pat, pattern, enl, dss));
}
static db::Edges corners_to_dots (const db::Region *r, double angle_start, double angle_end, bool include_angle_start, bool include_angle_end)
static db::Edges corners_to_dots (const db::Region *r, double angle_start, double angle_end, bool include_angle_start, bool include_angle_end, bool inverse, bool absolute)
{
return r->processed (db::CornersAsDots (angle_start, include_angle_start, angle_end, include_angle_end));
return r->processed (db::CornersAsDots (angle_start, include_angle_start, angle_end, include_angle_end, inverse, absolute));
}
static db::Region corners_to_boxes (const db::Region *r, double angle_start, double angle_end, db::Coord dim, bool include_angle_start, bool include_angle_end)
static db::Region corners_to_boxes (const db::Region *r, double angle_start, double angle_end, db::Coord dim, bool include_angle_start, bool include_angle_end, bool inverse, bool absolute)
{
return r->processed (db::CornersAsRectangles (angle_start, include_angle_start, angle_end, include_angle_end, dim));
return r->processed (db::CornersAsRectangles (angle_start, include_angle_start, angle_end, include_angle_end, inverse, absolute, dim));
}
static db::EdgePairs corners_to_edge_pairs (const db::Region *r, double angle_start, double angle_end, bool include_angle_start, bool include_angle_end)
static db::EdgePairs corners_to_edge_pairs (const db::Region *r, double angle_start, double angle_end, bool include_angle_start, bool include_angle_end, bool inverse, bool absolute)
{
return r->processed (db::CornersAsEdgePairs (angle_start, include_angle_start, angle_end, include_angle_end));
return r->processed (db::CornersAsEdgePairs (angle_start, include_angle_start, angle_end, include_angle_end, inverse, absolute));
}
static db::Region *new_si (const db::RecursiveShapeIterator &si)
@ -1697,7 +1697,7 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
"@hide\n"
"This method is provided for DRC implementation.\n"
) +
method_ext ("corners", &corners_to_boxes, gsi::arg ("angle_min", -180.0), gsi::arg ("angle_max", 180.0), gsi::arg ("dim", 1), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", true),
method_ext ("corners", &corners_to_boxes, gsi::arg ("angle_min", -180.0), gsi::arg ("angle_max", 180.0), gsi::arg ("dim", 1), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", true), gsi::arg ("inverse", false), gsi::arg ("absolute", false),
"@brief This method will select all corners whose attached edges satisfy the angle condition.\n"
"\n"
"The angle values specify a range of angles: all corners whose attached edges form an angle "
@ -1706,29 +1706,35 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
"If 'include_angle_min' is true, the angle condition is >= min. angle, otherwise it is > min. angle. "
"Same for 'include_angle_,ax' and the max. angle.\n"
"\n"
"The angle is measured "
"With 'absolute' set to false (the default), the angle is measured "
"between the incoming and the outcoming edge in mathematical sense: a positive value is a turn left "
"while a negative value is a turn right. Since polygon contours are oriented clockwise, positive "
"angles will report concave corners while negative ones report convex ones.\n"
"With the 'absolute' option set to true, there is no such distinction and angle values are always positive.\n"
"\n"
"With 'inverse' set to true, the method will select corners not meeting the angle criterion.\n"
"\n"
"A similar function that reports corners as point-like edges is \\corners_dots.\n"
"\n"
"This method has been introduced in version 0.25. 'include_min_angle' and 'include_max_angle' have been added in version 0.27.\n"
"This method has been introduced in version 0.25. 'include_min_angle' and 'include_max_angle' have been added in version 0.27. "
"'inverse' and 'absolute' have been added in version 0.29.1.\n"
) +
method_ext ("corners_dots", &corners_to_dots, gsi::arg ("angle_start", -180.0), gsi::arg ("angle_end", 180.0), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", true),
method_ext ("corners_dots", &corners_to_dots, gsi::arg ("angle_start", -180.0), gsi::arg ("angle_end", 180.0), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", true), gsi::arg ("inverse", false), gsi::arg ("absolute", false),
"@brief This method will select all corners whose attached edges satisfy the angle condition.\n"
"\n"
"This method is similar to \\corners, but delivers an \\Edges collection with dot-like edges for each corner.\n"
"\n"
"This method has been introduced in version 0.25. 'include_min_angle' and 'include_max_angle' have been added in version 0.27.\n"
"This method has been introduced in version 0.25. 'include_min_angle' and 'include_max_angle' have been added in version 0.27. "
"'inverse' and 'absolute' have been added in version 0.29.1.\n"
) +
method_ext ("corners_edge_pairs", &corners_to_edge_pairs, gsi::arg ("angle_start", -180.0), gsi::arg ("angle_end", 180.0), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", true),
method_ext ("corners_edge_pairs", &corners_to_edge_pairs, gsi::arg ("angle_start", -180.0), gsi::arg ("angle_end", 180.0), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", true), gsi::arg ("inverse", false), gsi::arg ("absolute", false),
"@brief This method will select all corners whose attached edges satisfy the angle condition.\n"
"\n"
"This method is similar to \\corners, but delivers an \\EdgePairs collection with an edge pairs for each corner.\n"
"The first edge is the incoming edge of the corner, the second one the outgoing edge.\n"
"\n"
"This method has been introduced in version 0.27.1.\n"
"This method has been introduced in version 0.27.1. "
"'inverse' and 'absolute' have been added in version 0.29.1.\n"
) +
method ("merge", (db::Region &(db::Region::*) ()) &db::Region::merge,
"@brief Merge the region\n"

View File

@ -1043,13 +1043,13 @@ TEST(21_Processors)
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (1, 0)), r1);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r1.processed (db::CornersAsDots (-180.0, true, 180.0, true)));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r1.processed (db::CornersAsDots (0.0, true, 180.0, true)));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r1.processed (db::CornersAsDots (-180.0, true, 180.0, true, false, false)));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r1.processed (db::CornersAsDots (0.0, true, 180.0, true, false, false)));
db::Region ext;
r1.processed (db::CornersAsDots (0.0, true, 180.0, true)).extended (ext, 1000, 1000, 2000, 2000);
r1.processed (db::CornersAsDots (0.0, true, 180.0, true, false, false)).extended (ext, 1000, 1000, 2000, 2000);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), ext);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), r1.processed (db::CornersAsRectangles (-180.0, true, 180.0, true, 2000)));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (14, 0)), r1.processed (db::CornersAsRectangles (0.0, true, 180.0, true, 2000)));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), r1.processed (db::CornersAsRectangles (-180.0, true, 180.0, true, false, false, 2000)));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (14, 0)), r1.processed (db::CornersAsRectangles (0.0, true, 180.0, true, false, false, 2000)));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r1.processed (db::extents_processor<db::Polygon> (0, 0)));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r1.processed (db::extents_processor<db::Polygon> (1000, 2000)));

View File

@ -836,10 +836,10 @@ void run_test15 (tl::TestBase *_this, bool deep)
db::CompoundRegionOperationPrimaryNode *primary = new db::CompoundRegionOperationPrimaryNode ();
db::CompoundRegionProcessingOperationNode *corners1 = new db::CompoundRegionProcessingOperationNode (new db::CornersAsRectangles (-180.0, true, 180.0, true, 1), primary, true /*processor is owned*/);
db::CompoundRegionProcessingOperationNode *corners1 = new db::CompoundRegionProcessingOperationNode (new db::CornersAsRectangles (-180.0, true, 180.0, true, false, false, 1), primary, true /*processor is owned*/);
db::CompoundRegionCountFilterNode count1 (corners1, false, 5, 10000);
db::CompoundRegionToEdgeProcessingOperationNode *corners2 = new db::CompoundRegionToEdgeProcessingOperationNode (new db::CornersAsDots (-180.0, true, 180.0, true), primary, true /*processor is owned*/);
db::CompoundRegionToEdgeProcessingOperationNode *corners2 = new db::CompoundRegionToEdgeProcessingOperationNode (new db::CornersAsDots (-180.0, true, 180.0, true, false, false), primary, true /*processor is owned*/);
db::CompoundRegionCountFilterNode count2 (corners2, true, 5, 10000);
EXPECT_EQ (count1.result_type () == db::CompoundRegionJoinOperationNode::Region, true);

View File

@ -327,8 +327,8 @@ TEST(5_Filters)
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (2, 0)), r2);
db::EdgeOrientationFilter eof1 (0, true, 1, true, false);
db::EdgeOrientationFilter eof2 (0, true, 1, true, true);
db::EdgeOrientationFilter eof1 (0, true, 1, true, false, false);
db::EdgeOrientationFilter eof2 (0, true, 1, true, true, false);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), e2.filtered (eof1));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), e2.filtered (eof2));

View File

@ -1264,13 +1264,13 @@ TEST(21_Processors)
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (1, 0)), r1);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r1.processed (db::CornersAsDots (-180.0, true, 180.0, true)));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r1.processed (db::CornersAsDots (0.0, true, 180.0, true)));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r1.processed (db::CornersAsDots (-180.0, true, 180.0, true, false, false)));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r1.processed (db::CornersAsDots (0.0, true, 180.0, true, false, false)));
db::Region ext;
r1.processed (db::CornersAsDots (0.0, true, 180.0, true)).extended (ext, 1000, 1000, 2000, 2000);
r1.processed (db::CornersAsDots (0.0, true, 180.0, true, false, false)).extended (ext, 1000, 1000, 2000, 2000);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), ext);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), r1.processed (db::CornersAsRectangles (-180.0, true, 180.0, true, 2000)));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (14, 0)), r1.processed (db::CornersAsRectangles (0.0, true, 180.0, true, 2000)));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), r1.processed (db::CornersAsRectangles (-180.0, true, 180.0, true, false, false, 2000)));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (14, 0)), r1.processed (db::CornersAsRectangles (0.0, true, 180.0, true, false, false, 2000)));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r1.processed (db::extents_processor<db::Polygon> (0, 0)));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r1.processed (db::extents_processor<db::Polygon> (1000, 2000)));

View File

@ -166,6 +166,7 @@ TEST(5_InternalAngleFilter)
{
db::EdgePair ep0 (db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (100, 0), db::Point (0, 0)));
db::EdgePair ep45 (db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 0), db::Point (100, 100)));
db::EdgePair ep45inv (db::Edge (db::Point (0, 0), db::Point (100, 100)), db::Edge (db::Point (0, 0), db::Point (100, 0)));
db::EdgePair ep180 (db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 0), db::Point (100, 0)));
db::EdgePair ep90 (db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 0), db::Point (0, 100)));
db::EdgePair epm90 (db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 100), db::Point (0, 0)));
@ -187,6 +188,7 @@ TEST(5_InternalAngleFilter)
EXPECT_EQ (db::InternalAngleEdgePairFilter (45.0, false).selected (ep90), false);
EXPECT_EQ (db::InternalAngleEdgePairFilter (45.0, false).selected (epm90), false);
EXPECT_EQ (db::InternalAngleEdgePairFilter (45.0, false).selected (ep45), true);
EXPECT_EQ (db::InternalAngleEdgePairFilter (45.0, false).selected (ep45inv), true);
EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true).selected (ep0), false);
EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true).selected (ep180), false);
@ -199,4 +201,12 @@ TEST(5_InternalAngleFilter)
EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, false).selected (ep90), false);
EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, false).selected (epm90), false);
EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, false).selected (ep45), true);
EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, false).selected (ep45inv), true);
EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, true).selected (ep0), false);
EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, true).selected (ep180), false);
EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, true).selected (ep90), true);
EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, true).selected (epm90), true);
EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, true).selected (ep45), false);
EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, true).selected (ep45inv), false);
}

View File

@ -202,55 +202,63 @@ TEST(4)
EXPECT_EQ (db::compare (rr, "(0,0;0,200);(0,200;100,200);(100,200;100,0);(100,0;0,0);(300,0;200,0)"), true);
}
{
db::EdgeOrientationFilter f1 (0.0, false);
db::EdgeOrientationFilter f1 (0.0, false, false);
EXPECT_EQ (db::compare (r.filtered (f1), "(0,200;100,200);(100,0;0,0);(300,0;200,0)"), true);
}
{
db::EdgeOrientationFilter f1 (50.0, true, 80.0, false, false);
db::EdgeOrientationFilter f1 (50.0, true, 80.0, false, false, false);
EXPECT_EQ (db::compare (r.filtered (f1), "(200,0;250,200);(250,-200;300,0)"), true);
}
{
db::EdgeOrientationFilter f1 (50.0, true, 80.0, false, true);
db::EdgeOrientationFilter f1 (-80.0, true, -50.0, false, false, false);
EXPECT_EQ (db::compare (r.filtered (f1), "(250,200;300,0);(200,0;250,-200)"), true);
}
{
db::EdgeOrientationFilter f1 (50.0, true, 80.0, false, false, true);
EXPECT_EQ (db::compare (r.filtered (f1), "(200,0;250,200);(250,200;300,0);(200,0;250,-200);(250,-200;300,0)"), true);
}
{
db::EdgeOrientationFilter f1 (50.0, true, 80.0, false, true, false);
EXPECT_EQ (db::compare (r.filtered (f1), "(0,0;0,200);(0,200;100,200);(100,200;100,0);(100,0;0,0);(250,200;300,0);(300,0;200,0);(200,0;250,-200)"), true);
}
{
db::EdgeOrientationFilter f1 (0.0, true, 1.0, false, false);
db::EdgeOrientationFilter f1 (0.0, true, 1.0, false, false, false);
EXPECT_EQ (db::compare (r.filtered (f1), "(0,200;100,200);(100,0;0,0);(300,0;200,0)"), true);
}
{
db::EdgeOrientationFilter f1 (-1.0, true, 1.0, false, false);
db::EdgeOrientationFilter f1 (-1.0, true, 1.0, false, false, false);
EXPECT_EQ (db::compare (r.filtered (f1), "(0,200;100,200);(100,0;0,0);(300,0;200,0)"), true);
}
{
db::EdgeOrientationFilter f1 (-1.0, true, 0.0, false, false);
db::EdgeOrientationFilter f1 (-1.0, true, 0.0, false, false, false);
EXPECT_EQ (r.filtered (f1).to_string (), "");
}
{
db::EdgeOrientationFilter f1 (-1.0, true, 0.0, true, false);
db::EdgeOrientationFilter f1 (-1.0, true, 0.0, true, false, false);
EXPECT_EQ (db::compare (r.filtered (f1), "(0,200;100,200);(100,0;0,0);(300,0;200,0)"), true);
}
{
db::EdgeOrientationFilter f1 (0.0, true, 1.0, true, false);
db::EdgeOrientationFilter f1 (0.0, true, 1.0, true, false, false);
EXPECT_EQ (db::compare (r.filtered (f1), "(0,200;100,200);(100,0;0,0);(300,0;200,0)"), true);
}
{
db::EdgeOrientationFilter f1 (0.0, false, 1.0, true, false);
db::EdgeOrientationFilter f1 (0.0, false, 1.0, true, false, false);
EXPECT_EQ (r.filtered (f1).to_string (), "");
}
{
db::EdgeOrientationFilter f1 (90.0, false);
db::EdgeOrientationFilter f1 (90.0, false, false);
EXPECT_EQ (db::compare (r.filtered (f1), "(0,0;0,200);(100,200;100,0)"), true);
}
{
db::EdgeOrientationFilter f1 (90.0, true, 91.0, false, false);
db::EdgeOrientationFilter f1 (90.0, true, 91.0, false, false, false);
EXPECT_EQ (db::compare (r.filtered (f1), "(0,0;0,200);(100,200;100,0)"), true);
}
{
db::EdgeOrientationFilter f1 (89.0, true, 91.0, false, false);
db::EdgeOrientationFilter f1 (89.0, true, 91.0, false, false, false);
EXPECT_EQ (db::compare (r.filtered (f1), "(0,0;0,200);(100,200;100,0)"), true);
}
{
db::EdgeOrientationFilter f1 (89.0, true, 90.0, false, false);
db::EdgeOrientationFilter f1 (89.0, true, 90.0, false, false, false);
EXPECT_EQ (r.filtered (f1).to_string (), "");
}
}

View File

@ -539,3 +539,27 @@ TEST(6_ReaderLog)
compare_text_files (path, au_path);
}
// issue #1696
TEST(7_CustomDevice)
{
db::LayoutToNetlist l2n;
std::string in_path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "l2n_reader_7.l2n");
tl::InputStream is_in (in_path);
db::LayoutToNetlistStandardReader reader (is_in);
reader.read (&l2n);
// verify against the input
std::string path = tmp_file ("tmp.txt");
{
tl::OutputStream stream (path);
db::LayoutToNetlistStandardWriter writer (stream, false);
writer.write (&l2n);
}
std::string au_path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "l2n_reader_au_7.l2n");
compare_text_files (path, au_path);
}

View File

@ -880,6 +880,40 @@ TEST(23_endl)
);
}
TEST(24_recursive_calls)
{
db::Netlist nl;
std::string path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader24.cir");
db::NetlistSpiceReader reader;
tl::InputStream is (path);
try {
reader.read (is, nl);
EXPECT_EQ (false, true);
} catch (tl::Exception &ex) {
EXPECT_EQ (ex.msg ().find ("Subcircuit 'C1' called recursively in"), size_t (0));
}
}
TEST(25_dismiss_top_level)
{
db::Netlist nl;
std::string path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader25.cir");
db::NetlistSpiceReader reader;
tl::InputStream is (path);
reader.read (is, nl);
EXPECT_EQ (nl.to_string (),
"circuit TOP (A=A,B=B);\n"
" device NMOS '1' (S=A,G=B,D=A,B=B) (L=100,W=100,AS=0,AD=0,PS=0,PD=0);\n"
"end;\n"
);
}
TEST(100_ExpressionParser)
{
std::map<std::string, tl::Variant> vars;

View File

@ -2574,17 +2574,17 @@ TEST(100_Processors)
r.insert (db::Box (db::Point (0, 300), db::Point (200, 400)));
r.insert (db::Box (db::Point (100, 300), db::Point (200, 500)));
EXPECT_EQ (db::compare (r.processed (db::CornersAsDots (-180.0, true, 180.0, true)), "(100,0;100,0);(0,0;0,0);(0,200;0,200);(100,200;100,200);(200,300;200,300);(0,300;0,300);(0,400;0,400);(100,400;100,400);(100,500;100,500);(200,500;200,500)"), true);
EXPECT_EQ (r.processed (db::CornersAsDots (0.0, true, 180.0, true)).to_string (), "(100,400;100,400)");
EXPECT_EQ (db::compare (r.processed (db::CornersAsDots (-90.0, true, 90.0, true)), "(100,0;100,0);(0,0;0,0);(0,200;0,200);(100,200;100,200);(200,300;200,300);(0,300;0,300);(0,400;0,400);(100,400;100,400);(100,500;100,500);(200,500;200,500)"), true);
EXPECT_EQ (r.processed (db::CornersAsDots (-90.0, false, 90.0, true)).to_string (), "(100,400;100,400)");
EXPECT_EQ (db::compare (r.processed (db::CornersAsDots (-90.0, true, 90.0, false)), "(100,0;100,0);(0,0;0,0);(0,200;0,200);(100,200;100,200);(200,300;200,300);(0,300;0,300);(0,400;0,400);(100,500;100,500);(200,500;200,500)"), true);
EXPECT_EQ (r.processed (db::CornersAsDots (-90.0, false, 90.0, false)).to_string (), "");
EXPECT_EQ (db::compare (r.processed (db::CornersAsDots (-180.0, true, 180.0, true, false, false)), "(100,0;100,0);(0,0;0,0);(0,200;0,200);(100,200;100,200);(200,300;200,300);(0,300;0,300);(0,400;0,400);(100,400;100,400);(100,500;100,500);(200,500;200,500)"), true);
EXPECT_EQ (r.processed (db::CornersAsDots (0.0, true, 180.0, true, false, false)).to_string (), "(100,400;100,400)");
EXPECT_EQ (db::compare (r.processed (db::CornersAsDots (-90.0, true, 90.0, true, false, false)), "(100,0;100,0);(0,0;0,0);(0,200;0,200);(100,200;100,200);(200,300;200,300);(0,300;0,300);(0,400;0,400);(100,400;100,400);(100,500;100,500);(200,500;200,500)"), true);
EXPECT_EQ (r.processed (db::CornersAsDots (-90.0, false, 90.0, true, false, false)).to_string (), "(100,400;100,400)");
EXPECT_EQ (db::compare (r.processed (db::CornersAsDots (-90.0, true, 90.0, false, false, false)), "(100,0;100,0);(0,0;0,0);(0,200;0,200);(100,200;100,200);(200,300;200,300);(0,300;0,300);(0,400;0,400);(100,500;100,500);(200,500;200,500)"), true);
EXPECT_EQ (r.processed (db::CornersAsDots (-90.0, false, 90.0, false, false, false)).to_string (), "");
db::Region ext;
r.processed (db::CornersAsDots (0.0, true, 180.0, true)).extended (ext, 10, 10, 20, 20);
r.processed (db::CornersAsDots (0.0, true, 180.0, true, false, false)).extended (ext, 10, 10, 20, 20);
EXPECT_EQ (ext.to_string (), "(90,380;90,420;110,420;110,380)");
EXPECT_EQ (db::compare (r.processed (db::CornersAsRectangles (-180.0, true, 180.0, true, 2)), "(98,-2;98,2;102,2;102,-2);(-2,-2;-2,2;2,2;2,-2);(-2,198;-2,202;2,202;2,198);(98,198;98,202;102,202;102,198);(198,298;198,302;202,302;202,298);(-2,298;-2,302;2,302;2,298);(-2,398;-2,402;2,402;2,398);(98,398;98,402;102,402;102,398);(98,498;98,502;102,502;102,498);(198,498;198,502;202,502;202,498)"), true);
EXPECT_EQ (r.processed (db::CornersAsRectangles (0.0, true, 180.0, true, 2)).to_string (), "(98,398;98,402;102,402;102,398)");
EXPECT_EQ (db::compare (r.processed (db::CornersAsRectangles (-180.0, true, 180.0, true, false, false, 2)), "(98,-2;98,2;102,2;102,-2);(-2,-2;-2,2;2,2;2,-2);(-2,198;-2,202;2,202;2,198);(98,198;98,202;102,202;102,198);(198,298;198,302;202,302;202,298);(-2,298;-2,302;2,302;2,298);(-2,398;-2,402;2,402;2,398);(98,398;98,402;102,402;102,398);(98,498;98,502;102,502;102,498);(198,498;198,502;202,502;202,498)"), true);
EXPECT_EQ (r.processed (db::CornersAsRectangles (0.0, true, 180.0, true, false, false, 2)).to_string (), "(98,398;98,402;102,402;102,398)");
EXPECT_EQ (db::compare (r.processed (db::extents_processor<db::Polygon> (0, 0)), "(0,0;0,200;100,200;100,0);(0,300;0,500;200,500;200,300)"), true);
EXPECT_EQ (db::compare (r.processed (db::extents_processor<db::Polygon> (10, 20)), "(-10,-20;-10,220;110,220;110,-20);(-10,280;-10,520;210,520;210,280)"), true);

View File

@ -164,7 +164,7 @@ out = in.drc((width &lt; 0.3).edges - secondary(waive))
This operation selects edges by their angle, measured against the horizontal
axis in the mathematical sense.
</p><p>
For this measurement edges are considered without their direction and straight lines.
For this measurement edges are considered without their direction.
A horizontal edge has an angle of zero degree. A vertical one has
an angle of 90 degrees. The angle range is from -90 (exclusive) to 90 degree (inclusive).
</p><p>
@ -172,20 +172,28 @@ If the input shapes are not polygons or edge pairs, they are converted to edges
before the angle test is made.
</p><p>
For example, the following code selects all edges from the primary shape which are 45 degree
(up) or 135 degree (down). The "+" will join the results:
(up) or -45 degree (down). The "+" operator will join the results:
</p><p>
<pre>
out = in.drc((angle == 45) + (angle == 135))
out = in.drc((primary.angle == 45) + (primary.angle == 135)) # equivalent
out = in.drc((angle == 45) + (angle == -45))
out = in.drc((primary.angle == 45) + (primary.angle == -45)) # equivalent
</pre>
</p><p>
Note that angle checks usually imply the need to rotation variant formation as cells which
You can avoid using both 45 and -45 degree checks with the 'absolute' option.
With this option, angles are not signed and the value is the absolute angle
the edge encloses with the x axis:
</p><p>
<pre>
out = in.drc(angle(absolute) == 45)
</pre>
</p><p>
Note that angle checks usually imply the need for rotation variant formation as cells which
are placed non-rotated and rotated by 90 degree cannot be considered identical. This imposes
a performance penalty in hierarchical mode. If possible, consider using <a href="/about/drc_ref_drc.xml#rectilinear">DRC#rectilinear</a> for
example to detect shapes with non-manhattan geometry instead of using angle checks.
</p><p>
The "angle" method is available as a plain function or as a method on <a href="/about/drc_ref_drc.xml">DRC</a> expressions.
The plain function is equivalent to "primary.angle".
The plain function is equivalent to the method call "primary.angle".
</p>
<a name="area"/><h2>"area" - Selects the primary shape if the area is meeting the condition</h2>
<keyword name="area"/>
@ -363,8 +371,7 @@ See <a href="/about/drc_ref_layer.xml#centers">layer#centers</a> for details abo
<p>Usage:</p>
<ul>
<li><tt>expression.corners</tt></li>
<li><tt>expression.corners(as_dots)</tt></li>
<li><tt>expression.corners(as_boxes)</tt></li>
<li><tt>expression.corners([ options ])</tt></li>
</ul>
<p>
This operation acts on polygons and selects the corners of the polygons.
@ -374,10 +381,20 @@ and negative for the turn to the right. Hence positive angles indicate concave
(inner) corners, negative ones indicate convex (outer) corners.
Angles take values between -180 and 180 degree.
</p><p>
When using "as_dots" for the argument, the operation will return single-point edges at
When using "as_dots" for an option, the operation will return single-point edges at
the selected corners. With "as_boxes" (the default), small (2x2 DBU) rectangles will be
produced at each selected corner.
</p><p>
Another option is "absolute" which selects the corners by absolute angle - i.e left
and right turns will both be considered positive angles.
</p><p>
Examples for use of the options are:
</p><p>
<pre>
corners(as_dots)
corners(as_boxes, absolute)
</pre>
</p><p>
The following example selects all corners:
</p><p>
<pre>

View File

@ -264,8 +264,11 @@ deliver objects that can be converted into polygons. Such objects are of class <
<p>
This method produces markers on the corners of the polygons. An angle criterion can be given which
selects corners based on the angle of the connecting edges. Positive angles indicate a left turn
while negative angles indicate a right turn. Since polygons are oriented clockwise, positive angles
while negative angles indicate a right turn.
Since polygons are oriented clockwise, positive angles
indicate concave (inner) corners while negative ones indicate convex (outer) corners
The 'absolute' option allows turning this off and considering both left and right turns
positive angles.
</p><p>
The markers generated can be point-like edges or small 2x2 DBU boxes. The latter is the default.
</p><p>
@ -276,6 +279,8 @@ The options available are:
<li><b>as_dots </b>: with this option, point-like edges will be produced instead of small boxes </li>
<li><b>as_edge_pairs </b>: with this option, an edge pair is produced for each corner selected. The first edge
is the incoming edge to the corner, the second edge the outgoing edge. </li>
<li><b>absolute </b>: with this option, left and right turns will both be considered positive angles </li>
<li><b>negative </b>: with this option, all corners <b>not </b>matching the angle criterion are selected </li>
</ul>
</p><p>
The following images show the effect of this method:
@ -3615,13 +3620,13 @@ The following images illustrate the effect of the "without_touching_corners" opt
<keyword name="with_angle"/>
<p>Usage:</p>
<ul>
<li><tt>layer.with_angle(min .. max)</tt></li>
<li><tt>layer.with_angle(value)</tt></li>
<li><tt>layer.with_angle(min, max)</tt></li>
<li><tt>layer.with_angle(min .. max [, absolute])</tt></li>
<li><tt>layer.with_angle(value [, absolute])</tt></li>
<li><tt>layer.with_angle(min, max [, absolute])</tt></li>
<li><tt>layer.with_angle(ortho)</tt></li>
<li><tt>layer.with_angle(diagonal)</tt></li>
<li><tt>layer.with_angle(diagonal_only)</tt></li>
<li><tt>edge_pair_layer.with_angle(... [, both])</tt></li>
<li><tt>edge_pair_layer.with_angle(... [, both] [, absolute])</tt></li>
</ul>
<p>
When called on an edge layer, the method selects edges by their angle,
@ -3641,6 +3646,11 @@ meeting the angle criterion. In this case an additional argument is accepted whi
either "both" (plain word) to indicate that both edges have to be within the given interval.
Without this argument, it is sufficient for one edge to meet the criterion.
</p><p>
The "absolute" option is available for edge or edge pair layers.
Without the "absolute" option, edges sloping down are assigned a negative angle while edges sloping up are assigned
a positive angle (vertical edges are always 90 degree). With the "absolute" option,
edges sloping down also have a positive angle which is the one enclosed with the horizontal axis.
</p><p>
Here are examples for "with_angle" on edge pair layers:
</p><p>
<pre>
@ -4006,21 +4016,25 @@ This method is available for polygon layers only.
<keyword name="without_angle"/>
<p>Usage:</p>
<ul>
<li><tt>layer.without_angle(min .. max)</tt></li>
<li><tt>layer.without_angle(value)</tt></li>
<li><tt>layer.without_angle(min, max)</tt></li>
<li><tt>layer.without_angle(min .. max [, absolute])</tt></li>
<li><tt>layer.without_angle(value [, absolute])</tt></li>
<li><tt>layer.without_angle(min, max [, absolute])</tt></li>
<li><tt>layer.without_angle(ortho)</tt></li>
<li><tt>layer.without_angle(diagonal)</tt></li>
<li><tt>layer.without_angle(diagonal_only)</tt></li>
<li><tt>edge_pair_layer.without_angle(... [, both])</tt></li>
<li><tt>edge_pair_layer.without_angle(... [, both] [, absolute])</tt></li>
</ul>
<p>
The method basically is the inverse of <a href="#with_angle">with_angle</a>. It selects all edges
of the edge layer or corners of the polygons which do not have the given angle (second form) or whose angle
is not inside the given interval (first and third form) or of the given type (other forms).
</p><p>
When called on edge pairs, it selects
edge pairs by the angles of their edges.
When called on edge pairs, it selects edge pairs by the angles of their edges.
</p><p>
The "absolute" option is available for edge or edge pair layers. Without the "absolute" option,
edges sloping down are assigned a negative angle while edges sloping up are assigned
a positive angle (vertical edges are always 90 degree). With the "absolute" option,
edges sloping down also have a positive angle which is the one enclosed with the horizontal axis.
</p><p>
A note on the "both" modifier (without_angle called on edge pairs): "both" means that
both edges need to be "without_angle". For example

View File

@ -282,6 +282,7 @@ var x = 3; x = x + 1; x
<tr><td><tt>combine(</tt>x<tt>,</tt>y<tt>)</tt></td><td>String</td><td>String</td><td>Combines the path components x and y using the system specific separator</td></tr>
<tr><td><tt>cosh(</tt>x<tt>)</tt></td><td>Numeric</td><td>Numeric</td><td>Hyperbolic cosine function</td></tr>
<tr><td><tt>cos(</tt>x<tt>)</tt></td><td>Numeric</td><td>Numeric</td><td>Cosine function</td></tr>
<tr><td><tt>downcase(</tt>x<tt>)</tt></td><td>String</td><td>String</td><td>Converts the given string to lower case</td></tr>
<tr><td><tt>env(</tt>x<tt>)</tt></td><td>String</td><td>String</td><td>Access an environment variable</td></tr>
<tr><td><tt>error(</tt>x<tt>)</tt></td><td>String</td><td></td><td>Raise an error</td></tr>
<tr><td><tt>exp(</tt>x<tt>)</tt></td><td>Numeric</td><td>Numeric</td><td>Exponential function</td></tr>
@ -318,6 +319,7 @@ var x = 3; x = x + 1; x
<tr><td><tt>to_f(</tt>x<tt>)</tt></td><td>Any</td><td>Numeric</td><td>Convert argument to numeric if possible</td></tr>
<tr><td><tt>to_i(</tt>x<tt>)</tt></td><td>Any</td><td>Numeric (integer)</td><td>Convert argument to numeric (32 bit integer)</td></tr>
<tr><td><tt>to_s(</tt>x<tt>)</tt></td><td>Any</td><td>String</td><td>Convert argument to string</td></tr>
<tr><td><tt>upcase(</tt>x<tt>)</tt></td><td>String</td><td>String</td><td>Converts the given string to upper case</td></tr>
</table>
</doc>

View File

@ -234,6 +234,49 @@ gate.width(min_width).output("gate_width", "Gate width violations")</pre>
Hence such files can only be run from the macro IDE.
</p>
<h2>DRC waiving flow</h2>
<p>
DRC waiving is a process of signing off DRC violations that cannot be avoided.
Usually DRC waiving is not encouraged, as manufacturability of the device cannot
be guaranteed if DRC violations are present. Even worse, giving a non-clean layout
into manufacturing may create a contamination risk that manufacturers will try
to avoid. Hence, non-DRC-clean layouts are usually rejected.
</p>
<p>
Still there are some legit reasons for DRC waiving. Sometimes DRC rules do not apply
because a specific technology option is not used by the device and corresponding
DRC rules do not apply. Or, a certain DRC rule may be a recommended rule, and violating
it is not forbidden. In that case, a DRC violation can be waived at your own risk.
Waiving is not a formal process. Usually, the manufacturer will ask for a confirmation
if DRC violations are present. KLayout can help documenting violations and copying
the waivers from one DRC run to the next.
</p>
<p>
The DRC waiving flow of KLayout is the following:
</p>
<ul>
<li>In the initial step, a RDB database is created by the DRC run, using the "report" command with a destination file.</li>
<li>This report is inspected in the marker browser. You can add comments, set flags and add screenshots. This is
also the time to waive DRC violations that you deem necessary to be waived. The marker browser has a "waive"
button which sets or resets the waived flag of the selected markers.</li>
<li>When finished, save the edited database to a 'waiver DB' using the marker browser's 'Save As Waiver DB' feature from
the File menu. Technically, this will write the report database to a second file. This second file is named
like the original one, with a ".w" added to the file name.</li>
<li>When you run the DRC again, KLayout will find this waiver DB file and apply attributes from the waiver DB
to the current report database. These attributes include the flags, images and comments.
This will - among the other annotations - apply the waived flag to the report database items.</li>
</ul>
<p>
In the waiving step, KLayout will apply attributes to items with the same value (i.e. shape), category and cell.
This specifically means, that when you rename a cell, waivers will no longer be applied, or - in the worst case -
be applied to the wrong cell. Hence, waiving should be done late in the process, when cell names are unlikely to change.
</p>
<h2>Using KLayout as a standalone DRC engine</h2>
<p>

View File

@ -691,7 +691,7 @@ CODE
# This operation selects edges by their angle, measured against the horizontal
# axis in the mathematical sense.
#
# For this measurement edges are considered without their direction and straight lines.
# For this measurement edges are considered without their direction.
# A horizontal edge has an angle of zero degree. A vertical one has
# an angle of 90 degrees. The angle range is from -90 (exclusive) to 90 degree (inclusive).
#
@ -699,23 +699,43 @@ CODE
# before the angle test is made.
#
# For example, the following code selects all edges from the primary shape which are 45 degree
# (up) or 135 degree (down). The "+" will join the results:
# (up) or -45 degree (down). The "+" operator will join the results:
#
# @code
# out = in.drc((angle == 45) + (angle == 135))
# out = in.drc((primary.angle == 45) + (primary.angle == 135)) # equivalent
# out = in.drc((angle == 45) + (angle == -45))
# out = in.drc((primary.angle == 45) + (primary.angle == -45)) # equivalent
# @/code
#
# Note that angle checks usually imply the need to rotation variant formation as cells which
# You can avoid using both 45 and -45 degree checks with the 'absolute' option.
# With this option, angles are not signed and the value is the absolute angle
# the edge encloses with the x axis:
#
# @code
# out = in.drc(angle(absolute) == 45)
# @/code
#
# Note that angle checks usually imply the need for rotation variant formation as cells which
# are placed non-rotated and rotated by 90 degree cannot be considered identical. This imposes
# a performance penalty in hierarchical mode. If possible, consider using \DRC#rectilinear for
# example to detect shapes with non-manhattan geometry instead of using angle checks.
#
# The "angle" method is available as a plain function or as a method on \DRC# expressions.
# The plain function is equivalent to "primary.angle".
# The plain function is equivalent to the method call "primary.angle".
def angle
DRCOpNodeEdgeOrientationFilter::new(@engine, self)
def angle(*args)
filter = DRCOpNodeEdgeOrientationFilter::new(@engine, self)
args.each do |a|
if a.is_a?(DRCAbsoluteMode)
filter.absolute = a.value
else
raise("Invalid argument (#{a.inspect}) for 'angle' method")
end
end
filter
end
# %DRC%
@ -758,8 +778,7 @@ CODE
# @name corners
# @brief Selects corners of polygons
# @synopsis expression.corners
# @synopsis expression.corners(as_dots)
# @synopsis expression.corners(as_boxes)
# @synopsis expression.corners([ options ])
#
# This operation acts on polygons and selects the corners of the polygons.
# It can be put into a condition to select corners by their angles. The angle of
@ -768,9 +787,19 @@ CODE
# (inner) corners, negative ones indicate convex (outer) corners.
# Angles take values between -180 and 180 degree.
#
# When using "as_dots" for the argument, the operation will return single-point edges at
# When using "as_dots" for an option, the operation will return single-point edges at
# the selected corners. With "as_boxes" (the default), small (2x2 DBU) rectangles will be
# produced at each selected corner.
#
# Another option is "absolute" which selects the corners by absolute angle - i.e left
# and right turns will both be considered positive angles.
#
# Examples for use of the options are:
#
# @code
# corners(as_dots)
# corners(as_boxes, absolute)
# @/code
#
# The following example selects all corners:
#
@ -789,14 +818,20 @@ CODE
# The "corners" method is available as a plain function or as a method on \DRC# expressions.
# The plain function is equivalent to "primary.corners".
def corners(output_mode = DRCOutputMode::new(:dots))
def corners(*args)
@engine._context("corners") do
if output_mode.is_a?(DRCOutputMode)
output_mode = output_mode.value
else
raise("Invalid argument (#{as_dots.inspect}) for 'corners' method")
output_mode = :as_boxes
absolute = false
args.each do |a|
if a.is_a?(DRCOutputMode)
output_mode = a.value
elsif a.is_a?(DRCAbsoluteMode)
absolute = a.value
else
raise("Invalid argument (#{a.inspect}) for 'corners' method")
end
end
DRCOpNodeCornersFilter::new(@engine, output_mode, self)
DRCOpNodeCornersFilter::new(@engine, output_mode, absolute, self)
end
end
@ -1721,6 +1756,7 @@ class DRCOpNodeEdgeOrientationFilter < DRCOpNodeWithCompare
attr_accessor :input
attr_accessor :inverse
attr_accessor :absolute
def initialize(engine, input)
super(engine)
@ -1748,6 +1784,7 @@ class DRCOpNodeEdgeOrientationFilter < DRCOpNodeWithCompare
args << (self.gt ? false : true)
args << (self.lt ? self.lt : (self.le ? self.le + angle_delta : 180.0))
args << (self.lt ? false : true)
args << self.absolute
RBA::CompoundRegionOperationNode::new_edge_orientation_filter(*args)
@ -2037,12 +2074,16 @@ class DRCOpNodeCornersFilter < DRCOpNodeWithCompare
attr_accessor :input
attr_accessor :output_mode
attr_accessor :inverse
attr_accessor :absolute
def initialize(engine, output_mode, input)
def initialize(engine, output_mode, absolute, input)
super(engine)
self.output_mode = output_mode
self.input = input
self.description = "corners"
self.inverse = false
self.absolute = absolute
end
def do_create_node(cache)
@ -2052,14 +2093,26 @@ class DRCOpNodeCornersFilter < DRCOpNodeWithCompare
args << (self.lt ? self.lt : (self.le ? self.le : 180.0))
args << (self.lt ? false : true)
if self.output_mode == :dots || self.output_mode == :edges
args << self.inverse
args << self.absolute
RBA::CompoundRegionOperationNode::new_corners_as_dots(*args)
elsif self.output_mode == :edge_pairs
args << self.inverse
args << self.absolute
RBA::CompoundRegionOperationNode::new_corners_as_edge_pairs(*args)
else
args << 2 # dimension is 2x2 DBU
args << self.inverse
args << self.absolute
RBA::CompoundRegionOperationNode::new_corners_as_rectangles(*args)
end
end
def inverted
res = self.dup
res.inverse = !res.inverse
return res
end
end

View File

@ -770,17 +770,7 @@ CODE
# \DRC# expressions (see \Layer#drc and \DRC#length for more details). In this context,
# the operation acts similar to \Layer#with_length.
# %DRC%
# @name angle
# @brief In universal DRC context: selects edges based on their orientation
# @synopsis angle (in condition)
#
# "angle" represents the edge orientation filter on the primary shape edges in
# \DRC# expressions (see \Layer#drc and \DRC#angle for more details). In this context,
# the operation acts similar to \Layer#with_angle.
%w(
angle
area
holes
hulls
@ -798,6 +788,15 @@ CODE
CODE
end
# %DRC%
# @name angle
# @brief In universal DRC context: selects edges based on their orientation
# @synopsis angle (in condition)
#
# "angle" represents the edge orientation filter on the primary shape edges in
# \DRC# expressions (see \Layer#drc and \DRC#angle for more details). In this context,
# the operation acts similar to \Layer#with_angle.
# %DRC%
# @name corners
# @brief Selects corners of polygons
@ -817,11 +816,6 @@ CODE
# The "corners" operator can be put into a condition which means it's
# applied to corners meeting a particular angle constraint.
def _cop_corners(output_mode = DRCOutputMode::new(:boxes))
# NOTE: this method is a fallback for the respective global ones which route to DRCLayer or here.
return primary.corners(output_mode)
end
# %DRC%
# @name extent_refs
# @brief Returns partial references to the boundings boxes of the polygons
@ -897,6 +891,8 @@ CODE
rounded_corners
sized
smoothed
corners
angle
).each do |f|
# NOTE: these methods are fallback for the respective global ones which route to DRCLayer or here.
eval <<"CODE"

View File

@ -115,11 +115,29 @@ module DRC
end
def write
if @file_name
rdb_file = @engine._make_path(@file_name)
# Apply waive DB if possible
wdb_file = rdb_file + ".w"
if File.exists?(wdb_file)
begin
wdb = RBA::ReportDatabase::new
wdb.load(wdb_file)
@engine.info("Applying waive database: #{wdb_file} ..")
@rdb.apply(wdb)
wdb._destroy
rescue
end
end
@engine.info("Writing report database: #{rdb_file} ..")
@rdb.save(rdb_file)
end
end
def rdb
@ -551,6 +569,10 @@ module DRC
end
end
def absolute
DRCAbsoluteMode::new(true)
end
def as_dots
DRCOutputMode::new(:dots)
end
@ -3226,7 +3248,33 @@ CODE
output_rdb = channel.rdb
output_cell = channel.cell
cat = output_rdb.create_category(args[0].to_s)
categories = args[0]
if !categories.is_a?(Array)
categories = [ categories.to_s ]
end
cat = nil
categories.each do |c|
ccat = nil
if cat
cat.each_sub_category do |i|
if i.name == c
ccat = i
break
end
end
else
output_rdb.each_category do |i|
if i.name == c
ccat = i
break
end
end
end
cat = ccat ? ccat : output_rdb.create_category(cat, c)
end
cat ||= output_rdb.create_category("default")
args[1] && cat.description = args[1]
cat.scan_collection(output_cell, RBA::CplxTrans::new(self.dbu), data)
@ -3333,7 +3381,7 @@ CODE
else
output_rdb = RBA::ReportDatabase::new(name)
end
cn = cellname && cellname.to_s
cn ||= @def_cell && @def_cell.name
cn ||= @def_source && @def_source.cell_obj && @def_source.cell_obj.name
@ -3343,7 +3391,8 @@ CODE
output_rdb.generator = self._generator
output_rdb.top_cell_name = cn
output_rdb.description = description
output_rdb.original_file = @def_path
RDBOutputChannel::new(self, output_rdb, output_rdb_index, cn, output_rdb_file)
end

View File

@ -861,13 +861,13 @@ CODE
# %DRC%
# @name with_angle
# @brief Selects edges by their angle
# @synopsis layer.with_angle(min .. max)
# @synopsis layer.with_angle(value)
# @synopsis layer.with_angle(min, max)
# @synopsis layer.with_angle(min .. max [, absolute])
# @synopsis layer.with_angle(value [, absolute])
# @synopsis layer.with_angle(min, max [, absolute])
# @synopsis layer.with_angle(ortho)
# @synopsis layer.with_angle(diagonal)
# @synopsis layer.with_angle(diagonal_only)
# @synopsis edge_pair_layer.with_angle(... [, both])
# @synopsis edge_pair_layer.with_angle(... [, both] [, absolute])
#
# When called on an edge layer, the method selects edges by their angle,
# measured against the horizontal axis in the mathematical sense.
@ -886,6 +886,11 @@ CODE
# either "both" (plain word) to indicate that both edges have to be within the given interval.
# Without this argument, it is sufficient for one edge to meet the criterion.
#
# The "absolute" option is available for edge or edge pair layers.
# Without the "absolute" option, edges sloping down are assigned a negative angle while edges sloping up are assigned
# a positive angle (vertical edges are always 90 degree). With the "absolute" option,
# edges sloping down also have a positive angle which is the one enclosed with the horizontal axis.
#
# Here are examples for "with_angle" on edge pair layers:
#
# @code
@ -931,21 +936,25 @@ CODE
# %DRC%
# @name without_angle
# @brief Selects edges by the their angle
# @synopsis layer.without_angle(min .. max)
# @synopsis layer.without_angle(value)
# @synopsis layer.without_angle(min, max)
# @synopsis layer.without_angle(min .. max [, absolute])
# @synopsis layer.without_angle(value [, absolute])
# @synopsis layer.without_angle(min, max [, absolute])
# @synopsis layer.without_angle(ortho)
# @synopsis layer.without_angle(diagonal)
# @synopsis layer.without_angle(diagonal_only)
# @synopsis edge_pair_layer.without_angle(... [, both])
# @synopsis edge_pair_layer.without_angle(... [, both] [, absolute])
#
# The method basically is the inverse of \with_angle. It selects all edges
# of the edge layer or corners of the polygons which do not have the given angle (second form) or whose angle
# is not inside the given interval (first and third form) or of the given type (other forms).
#
# When called on edge pairs, it selects
# edge pairs by the angles of their edges.
# When called on edge pairs, it selects edge pairs by the angles of their edges.
#
# The "absolute" option is available for edge or edge pair layers. Without the "absolute" option,
# edges sloping down are assigned a negative angle while edges sloping up are assigned
# a positive angle (vertical edges are always 90 degree). With the "absolute" option,
# edges sloping down also have a positive angle which is the one enclosed with the horizontal axis.
#
# A note on the "both" modifier (without_angle called on edge pairs): "both" means that
# both edges need to be "without_angle". For example
#
@ -989,57 +998,101 @@ CODE
# The method basically is the inverse of \with_internal_angle. It selects all
# edge pairs by the angle enclosed by their edges, applying the opposite criterion than \with_internal_angle.
%w(angle internal_angle).each do |f|
[true, false].each do |inv|
mn = (inv ? "without" : "with") + "_" + f
eval <<"CODE"
def #{mn}(*args)
[true, false].each do |inv|
mn = (inv ? "without" : "with") + "_angle"
eval <<"CODE"
def #{mn}(*args)
@engine._context("#{mn}") do
@engine._context("#{mn}") do
f = :with_#{f}
self.data.is_a?(RBA::Region) || self.data.is_a?(RBA::Edges) || self.data.is_a?(RBA::EdgePairs) || raise("Requires an edge, edge pair or polygon layer")
if "#{f}" == "angle"
self.data.is_a?(RBA::Region) || self.data.is_a?(RBA::Edges) || self.data.is_a?(RBA::EdgePairs) || raise("Requires an edge, edge pair or polygon layer")
args = args.select do |a|
if a.is_a?(DRCBothEdges)
if !self.data.is_a?(RBA::EdgePairs)
raise("'both' keyword is only available for edge pair layers")
end
f = :with_#{f}_both
false
else
true
end
absolute = false
both = false
args = args.select do |a|
if a.is_a?(DRCBothEdges)
if !self.data.is_a?(RBA::EdgePairs)
raise("'both' keyword is only available for edge pair layers")
end
else
requires_edge_pairs
end
result_class = self.data.is_a?(RBA::Edges) ? RBA::Edges : RBA::EdgePairs
if args.size == 1
a = args[0]
if a.is_a?(Range)
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a.begin, a.end, #{inv.inspect}))
elsif a.is_a?(DRCOrthoEdges) || a.is_a?(DRCDiagonalOnlyEdges) || a.is_a?(DRCDiagonalEdges)
if self.data.is_a?(RBA::Region)
raise("'ortho', 'diagonal' or 'diagonal_only' keyword is only available for edge or edge pair layers")
end
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a.value, #{inv.inspect}))
else
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a, #{inv.inspect}))
both = true
false
elsif a.is_a?(DRCAbsoluteMode)
if self.data.is_a?(RBA::Region)
raise("'absolute' keyword is only available for edge or edge pair layers")
end
elsif args.size == 2
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, args[0], args[1], #{inv.inspect}))
absolute = a.value
false
else
raise("Invalid number of range arguments (1 or 2 expected)")
true
end
end
if both
f = absolute ? :with_abs_angle_both : :with_angle_both
else
f = absolute ? :with_abs_angle : :with_angle
end
result_class = self.data.is_a?(RBA::Edges) ? RBA::Edges : RBA::EdgePairs
if args.size == 1
a = args[0]
if a.is_a?(Range)
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a.begin, a.end, #{inv.inspect}))
elsif a.is_a?(DRCOrthoEdges) || a.is_a?(DRCDiagonalOnlyEdges) || a.is_a?(DRCDiagonalEdges)
if self.data.is_a?(RBA::Region)
raise("'ortho', 'diagonal' or 'diagonal_only' keyword is only available for edge or edge pair layers")
end
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a.value, #{inv.inspect}))
else
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, @engine._make_numeric_value(a), #{inv.inspect}))
end
elsif args.size == 2
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, @engine._make_numeric_value(args[0]), @engine._make_numeric_value(args[1]), #{inv.inspect}))
else
raise("Invalid number of range arguments (1 or 2 expected)")
end
end
CODE
end
CODE
end
[true, false].each do |inv|
mn = (inv ? "without" : "with") + "_internal_angle"
eval <<"CODE"
def #{mn}(*args)
@engine._context("#{mn}") do
f = :with_internal_angle
requires_edge_pairs
result_class = self.data.is_a?(RBA::Edges) ? RBA::Edges : RBA::EdgePairs
if args.size == 1
a = args[0]
if a.is_a?(Range)
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a.begin, a.end, #{inv.inspect}))
elsif a.is_a?(DRCOrthoEdges) || a.is_a?(DRCDiagonalOnlyEdges) || a.is_a?(DRCDiagonalEdges)
if self.data.is_a?(RBA::Region)
raise("'ortho', 'diagonal' or 'diagonal_only' keyword is only available for edge or edge pair layers")
end
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, a.value, #{inv.inspect}))
else
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, @engine._make_numeric_value(a), #{inv.inspect}))
end
elsif args.size == 2
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, result_class, f, @engine._make_numeric_value(args[0]), @engine._make_numeric_value(args[1]), #{inv.inspect}))
else
raise("Invalid number of range arguments (1 or 2 expected)")
end
end
end
CODE
end
# %DRC%
@ -1231,8 +1284,11 @@ CODE
#
# This method produces markers on the corners of the polygons. An angle criterion can be given which
# selects corners based on the angle of the connecting edges. Positive angles indicate a left turn
# while negative angles indicate a right turn. Since polygons are oriented clockwise, positive angles
# while negative angles indicate a right turn.
# Since polygons are oriented clockwise, positive angles
# indicate concave (inner) corners while negative ones indicate convex (outer) corners
# The 'absolute' option allows turning this off and considering both left and right turns
# positive angles.
#
# The markers generated can be point-like edges or small 2x2 DBU boxes. The latter is the default.
#
@ -1243,6 +1299,8 @@ CODE
# @li @b as_dots @/b: with this option, point-like edges will be produced instead of small boxes @/li
# @li @b as_edge_pairs @/b: with this option, an edge pair is produced for each corner selected. The first edge
# is the incoming edge to the corner, the second edge the outgoing edge. @/li
# @li @b absolute @/b: with this option, left and right turns will both be considered positive angles @/li
# @li @b negative @/b: with this option, all corners @b not @/b matching the angle criterion are selected @/li
# @/ul
#
# The following images show the effect of this method:
@ -1264,6 +1322,8 @@ CODE
output_mode = :boxes
amin = -180.0
amax = 180.0
absolute = false
inverse = false
args.each do |a|
if a.is_a?(Range)
@ -1277,21 +1337,35 @@ CODE
amax = a.to_f
elsif a.is_a?(DRCOutputMode)
output_mode = a.value
elsif a.is_a?(DRCAbsoluteMode)
absolute = a.value
elsif a.is_a?(DRCNegative)
inverse = true
else
raise("Invalid argument #{a.inspect}")
end
end
f = :corners
cls = RBA::Region
args = [ amin, amax ]
if output_mode == :edges || output_mode == :dots
f = :corners_dots
cls = RBA::Edges
elsif output_mode == :edge_pairs
f = :corners_edge_pairs
cls = RBA::EdgePairs
else
f = :corners
cls = RBA::Region
args << 1 # 2x2 DBU boxes
end
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, cls, f, amin, amax))
args << true # include amin
args << true # include amax
args << inverse
args << absolute
DRCLayer::new(@engine, @engine._tcmd(self.data, 0, cls, f, *args))
end
@ -5111,7 +5185,11 @@ CODE
# This method will copy the content of the layer to the specified output.
#
# If a report database is selected for the output, the specification has to include a
# category name and optionally a category description.
# category name and optionally a category description. The category name can be an
# array of strings - in that case, a hierarchy of categories is created
# with the first array item being the top level category name.
# Shapes are added to an existing category, if a category with the given
# name already exists.
#
# If the layout is selected for the output, the specification can consist of
# one to three parameters: a layer number, a data type (optional, default is 0)

View File

@ -123,6 +123,16 @@ module DRC
end
end
# A wrapper for the "absolute" flag for
# some DRC functions. The purpose of this class
# is to identify the value by the class.
class DRCAbsoluteMode
attr_accessor :value
def initialize(v)
self.value = v
end
end
# A wrapper for a rectangle error filter mode
# The purpose of this wrapper is to identify the error filter mode
class DRCRectangleErrorFilter

View File

@ -25,6 +25,7 @@
#include "dbTestSupport.h"
#include "dbNetlist.h"
#include "dbNetlistSpiceReader.h"
#include "rdb.h"
#include "lymMacro.h"
#include "tlFileUtils.h"
@ -1673,6 +1674,16 @@ TEST(92_issue1594_dual_top)
compare_netlists (_this, output, au);
}
TEST(93_withAngle)
{
run_test (_this, "93", false);
}
TEST(93d_withAngle)
{
run_test (_this, "93", true);
}
TEST(100_edge_interaction_with_count)
{
run_test (_this, "100", false);
@ -1702,3 +1713,117 @@ TEST(102d_edge_modes)
{
run_test (_this, "102", true);
}
TEST(110_RDBVariantAssignment)
{
std::string rs = tl::testdata ();
rs += "/drc/drcSimpleTests_110.drc";
// apart from that it's a variant of 14b ...
std::string input = tl::testdata ();
input += "/drc/drcSimpleTests_110.gds";
std::string au_report = tl::testdata ();
au_report += "/drc/drcSimpleTests_au110.lyrdb";
std::string report = this->tmp_file ("tmp.lydrc");
{
// Set some variables
lym::Macro config;
config.set_text (tl::sprintf (
"$drc_force_gc = true\n"
"$drc_test_source = '%s'\n"
"$drc_test_report = '%s'\n"
, input, report)
);
config.set_interpreter (lym::Macro::Ruby);
EXPECT_EQ (config.run (), 0);
}
lym::Macro drc;
drc.load_from (rs);
EXPECT_EQ (drc.run (), 0);
compare_text_files (report, au_report);
}
TEST(111_RDBCategoryHierarchy)
{
std::string rs = tl::testdata ();
rs += "/drc/drcSimpleTests_111.drc";
// apart from that it's a variant of 14b ...
std::string input = tl::testdata ();
input += "/drc/drcSimpleTests_111.gds";
std::string au_report = tl::testdata ();
au_report += "/drc/drcSimpleTests_au111.lyrdb";
std::string report = this->tmp_file ("tmp.lydrc");
{
// Set some variables
lym::Macro config;
config.set_text (tl::sprintf (
"$drc_force_gc = true\n"
"$drc_test_source = '%s'\n"
"$drc_test_report = '%s'\n"
, input, report)
);
config.set_interpreter (lym::Macro::Ruby);
EXPECT_EQ (config.run (), 0);
}
lym::Macro drc;
drc.load_from (rs);
EXPECT_EQ (drc.run (), 0);
compare_text_files (report, au_report);
}
TEST(112_Waiving)
{
std::string rs = tl::testdata ();
rs += "/drc/drcSimpleTests_112.drc";
// apart from that it's a variant of 14b ...
std::string input = tl::testdata ();
input += "/drc/drcSimpleTests_112.gds";
std::string au_report = tl::testdata ();
au_report += "/drc/drcSimpleTests_au112.lyrdb";
std::string report = this->tmp_file ("tmp.lydrc");
{
// Set some variables
lym::Macro config;
config.set_text (tl::sprintf (
"$drc_force_gc = true\n"
"$drc_test_source = '%s'\n"
"$drc_test_report = '%s'\n"
, input, report)
);
config.set_interpreter (lym::Macro::Ruby);
EXPECT_EQ (config.run (), 0);
}
// prepare a waiver db
{
std::string report_w = this->tmp_file ("tmp.lydrc.w");
rdb::Database rdb_w;
rdb_w.load (au_report + ".w");
rdb_w.write (report_w);
}
lym::Macro drc;
drc.load_from (rs);
EXPECT_EQ (drc.run (), 0);
compare_text_files (report, au_report);
}

View File

@ -292,6 +292,98 @@ to load a marker database</string>
</widget>
</item>
</layout>
<action name="open_action">
<property name="text">
<string>Open</string>
</property>
<property name="shortcut">
<string>Ctrl+O</string>
</property>
<property name="menuRole">
<enum>QAction::NoRole</enum>
</property>
</action>
<action name="saveas_action">
<property name="text">
<string>Save As</string>
</property>
<property name="menuRole">
<enum>QAction::NoRole</enum>
</property>
</action>
<action name="reload_action">
<property name="text">
<string>Reload</string>
</property>
<property name="shortcut">
<string>F5</string>
</property>
<property name="menuRole">
<enum>QAction::NoRole</enum>
</property>
</action>
<action name="save_action">
<property name="text">
<string>Save</string>
</property>
<property name="shortcut">
<string>Ctrl+S</string>
</property>
<property name="menuRole">
<enum>QAction::NoRole</enum>
</property>
</action>
<action name="export_action">
<property name="text">
<string>Export To Layout</string>
</property>
<property name="menuRole">
<enum>QAction::NoRole</enum>
</property>
</action>
<action name="unload_action">
<property name="text">
<string>Unload</string>
</property>
<property name="menuRole">
<enum>QAction::NoRole</enum>
</property>
</action>
<action name="unload_all_action">
<property name="text">
<string>Unload All</string>
</property>
<property name="toolTip">
<string>Unload All</string>
</property>
<property name="menuRole">
<enum>QAction::NoRole</enum>
</property>
</action>
<action name="info_action">
<property name="text">
<string>Info</string>
</property>
<property name="menuRole">
<enum>QAction::NoRole</enum>
</property>
</action>
<action name="saveas_waiver_db_action">
<property name="text">
<string>Save As Waiver DB</string>
</property>
<property name="menuRole">
<enum>QAction::NoRole</enum>
</property>
</action>
<action name="apply_waiver_db_action">
<property name="text">
<string>Apply Waiver DB</string>
</property>
<property name="menuRole">
<enum>QAction::NoRole</enum>
</property>
</action>
</widget>
<customwidgets>
<customwidget>

View File

@ -480,51 +480,7 @@
<property name="spacing">
<number>6</number>
</property>
<item row="2" column="3">
<widget class="QToolButton" name="waive_pb">
<property name="toolTip">
<string>Waive</string>
</property>
<property name="text">
<string>W</string>
</property>
<property name="icon">
<iconset resource="../../icons/icons.qrc">
<normaloff>:/waived_16px.png</normaloff>:/waived_16px.png</iconset>
</property>
</widget>
</item>
<item row="2" column="5">
<widget class="QToolButton" name="nophoto_pb">
<property name="toolTip">
<string>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'DejaVu Sans'; font-size:10pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Remove snapshot&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../icons/icons.qrc">
<normaloff>:/nophoto_16px.png</normaloff>:/nophoto_16px.png</iconset>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QToolButton" name="flags_pb">
<property name="toolTip">
<string>Set or reset flag</string>
</property>
<property name="text">
<string>Flag</string>
</property>
<property name="popupMode">
<enum>QToolButton::MenuButtonPopup</enum>
</property>
</widget>
</item>
<item row="2" column="6">
<item row="2" column="7">
<widget class="QToolButton" name="photo_pb">
<property name="toolTip">
<string>Add snapshot</string>
@ -538,28 +494,24 @@ p, li { white-space: pre-wrap; }
</property>
</widget>
</item>
<item row="2" column="4">
<widget class="QToolButton" name="important_pb">
<item row="2" column="3">
<widget class="QToolButton" name="waive_pb">
<property name="toolTip">
<string>Important</string>
<string>Waive</string>
</property>
<property name="text">
<string>Imp</string>
<string>W</string>
</property>
<property name="icon">
<iconset resource="../../icons/icons.qrc">
<normaloff>:/important_16px.png</normaloff>:/important_16px.png</iconset>
<normaloff>:/waived_16px.png</normaloff>:/waived_16px.png</iconset>
</property>
<property name="shortcut">
<string>Ctrl+W</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="info_label">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0" colspan="7">
<item row="1" column="0" colspan="8">
<widget class="QFrame" name="frame_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@ -605,7 +557,38 @@ p, li { white-space: pre-wrap; }
</layout>
</widget>
</item>
<item row="0" column="0" colspan="7">
<item row="2" column="5">
<widget class="QToolButton" name="important_pb">
<property name="toolTip">
<string>Important</string>
</property>
<property name="text">
<string>Imp</string>
</property>
<property name="icon">
<iconset resource="../../icons/icons.qrc">
<normaloff>:/important_16px.png</normaloff>:/important_16px.png</iconset>
</property>
</widget>
</item>
<item row="2" column="6">
<widget class="QToolButton" name="nophoto_pb">
<property name="toolTip">
<string>&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'DejaVu Sans'; font-size:10pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Remove snapshot&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../icons/icons.qrc">
<normaloff>:/nophoto_16px.png</normaloff>:/nophoto_16px.png</iconset>
</property>
</widget>
</item>
<item row="0" column="0" colspan="8">
<widget class="QFrame" name="frame_6">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@ -662,6 +645,43 @@ p, li { white-space: pre-wrap; }
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="info_label">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QToolButton" name="flags_pb">
<property name="toolTip">
<string>Set or reset flag</string>
</property>
<property name="text">
<string>Flag</string>
</property>
<property name="popupMode">
<enum>QToolButton::MenuButtonPopup</enum>
</property>
</widget>
</item>
<item row="2" column="4">
<widget class="QToolButton" name="edit_pb">
<property name="toolTip">
<string>Edit Comment</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../icons/icons.qrc">
<normaloff>:/edit_16px.png</normaloff>:/edit_16px.png</iconset>
</property>
<property name="shortcut">
<string>F2</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>

View File

@ -24,6 +24,7 @@
#include <QDialog>
#include <QApplication>
#include <QCloseEvent>
#include "layBrowser.h"
#include "layLayoutViewBase.h"
@ -81,7 +82,7 @@ Browser::closeEvent (QCloseEvent *event)
if (active ()) {
m_active = false;
deactivated ();
QDialog::closeEvent (event);
event->accept ();
}
}

View File

@ -33,9 +33,11 @@
#include "layConverters.h"
#include "layQtTools.h"
#include "layConfigurationDialog.h"
#include "layBrowserDialog.h"
#include "dbLayoutUtils.h"
#include "dbRecursiveShapeIterator.h"
#include "dbStream.h"
#include "tlFileUtils.h"
#include "ui_MarkerBrowserDialog.h"
@ -84,34 +86,35 @@ MarkerBrowserDialog::MarkerBrowserDialog (lay::Dispatcher *root, lay::LayoutView
view ()->rdb_list_changed_event.add (this, &MarkerBrowserDialog::rdbs_changed);
}
m_open_action = new QAction (QObject::tr ("Open"), mp_ui->file_menu);
m_saveas_action = new QAction (QObject::tr ("Save As"), mp_ui->file_menu);
m_export_action = new QAction (QObject::tr ("Export To Layout"), mp_ui->file_menu);
m_reload_action = new QAction (QObject::tr ("Reload"), mp_ui->file_menu);
m_unload_action = new QAction (QObject::tr ("Unload"), mp_ui->file_menu);
m_unload_all_action = new QAction (QObject::tr ("Unload All"), mp_ui->file_menu);
connect (mp_ui->open_action, SIGNAL (triggered ()), this, SLOT (open_clicked ()));
connect (mp_ui->save_action, SIGNAL (triggered ()), this, SLOT (save_clicked ()));
connect (mp_ui->saveas_action, SIGNAL (triggered ()), this, SLOT (saveas_clicked ()));
connect (mp_ui->saveas_waiver_db_action, SIGNAL (triggered ()), this, SLOT (saveas_waiver_db_clicked ()));
connect (mp_ui->apply_waiver_db_action, SIGNAL (triggered ()), this, SLOT (apply_waiver_db_clicked ()));
connect (mp_ui->export_action, SIGNAL (triggered ()), this, SLOT (export_clicked ()));
connect (mp_ui->reload_action, SIGNAL (triggered ()), this, SLOT (reload_clicked ()));
connect (mp_ui->info_action, SIGNAL (triggered ()), this, SLOT (info_clicked ()));
connect (mp_ui->unload_action, SIGNAL (triggered ()), this, SLOT (unload_clicked ()));
connect (mp_ui->unload_all_action, SIGNAL (triggered ()), this, SLOT (unload_all_clicked ()));
connect (m_open_action, SIGNAL (triggered ()), this, SLOT (open_clicked ()));
connect (m_saveas_action, SIGNAL (triggered ()), this, SLOT (saveas_clicked ()));
connect (m_export_action, SIGNAL (triggered ()), this, SLOT (export_clicked ()));
connect (m_reload_action, SIGNAL (triggered ()), this, SLOT (reload_clicked ()));
connect (m_unload_action, SIGNAL (triggered ()), this, SLOT (unload_clicked ()));
connect (m_unload_all_action, SIGNAL (triggered ()), this, SLOT (unload_all_clicked ()));
mp_ui->file_menu->addAction (m_open_action);
mp_ui->file_menu->addAction (m_saveas_action);
mp_ui->file_menu->addAction (mp_ui->open_action);
mp_ui->file_menu->addAction (mp_ui->save_action);
mp_ui->file_menu->addAction (mp_ui->saveas_action);
mp_ui->file_menu->addAction (mp_ui->saveas_waiver_db_action);
mp_ui->file_menu->addAction (mp_ui->apply_waiver_db_action);
QAction *sep0 = new QAction (mp_ui->file_menu);
sep0->setSeparator (true);
mp_ui->file_menu->addAction (m_export_action);
mp_ui->file_menu->addAction (mp_ui->export_action);
QAction *sep1 = new QAction (mp_ui->file_menu);
sep1->setSeparator (true);
mp_ui->file_menu->addAction (sep1);
mp_ui->file_menu->addAction (m_reload_action);
mp_ui->file_menu->addAction (mp_ui->reload_action);
mp_ui->file_menu->addAction (mp_ui->info_action);
QAction *sep2 = new QAction (mp_ui->file_menu);
sep2->setSeparator (true);
mp_ui->file_menu->addAction (sep2);
mp_ui->file_menu->addAction (m_unload_action);
mp_ui->file_menu->addAction (m_unload_all_action);
mp_ui->file_menu->addAction (mp_ui->unload_action);
mp_ui->file_menu->addAction (mp_ui->unload_all_action);
connect (mp_ui->layout_cb, SIGNAL (activated (int)), this, SLOT (cv_index_changed (int)));
connect (mp_ui->rdb_cb, SIGNAL (activated (int)), this, SLOT (rdb_index_changed (int)));
@ -367,6 +370,99 @@ BEGIN_PROTECTED
END_PROTECTED
}
void
MarkerBrowserDialog::save_clicked ()
{
BEGIN_PROTECTED
if (m_rdb_index < int (view ()->num_rdbs ()) && m_rdb_index >= 0) {
rdb::Database *rdb = view ()->get_rdb (m_rdb_index);
if (rdb) {
if (rdb->filename ().empty ()) {
saveas_clicked ();
} else {
rdb->save (rdb->filename ());
rdb->reset_modified ();
}
}
}
END_PROTECTED
}
void
MarkerBrowserDialog::apply_waiver_db_clicked ()
{
BEGIN_PROTECTED
rdb::Database *rdb = 0;
if (m_rdb_index < int (view ()->num_rdbs ()) && m_rdb_index >= 0) {
rdb = view ()->get_rdb (m_rdb_index);
}
if (! rdb) {
return;
}
std::string wdb_filename;
if (! rdb->filename ().empty () && tl::file_exists (rdb->filename () + ".w")) {
wdb_filename = rdb->filename () + ".w";
} else {
// prepare and open the file dialog
lay::FileDialog open_dialog (this, tl::to_string (QObject::tr ("Apply Waiver DB File")), "Waiver DB files (*.w)");
if (! rdb->filename ().empty ()) {
wdb_filename = rdb->filename () + ".w";
}
if (! open_dialog.get_open (wdb_filename)) {
return;
}
}
rdb::Database wdb;
wdb.load (wdb_filename);
mp_ui->browser_frame->set_rdb (0);
rdb->apply (wdb);
mp_ui->browser_frame->set_rdb (rdb);
END_PROTECTED
}
void
MarkerBrowserDialog::saveas_waiver_db_clicked ()
{
BEGIN_PROTECTED
rdb::Database *rdb = 0;
if (m_rdb_index < int (view ()->num_rdbs ()) && m_rdb_index >= 0) {
rdb = view ()->get_rdb (m_rdb_index);
}
if (! rdb) {
return;
}
if (rdb->filename ().empty ()) {
throw tl::Exception (tl::to_string (tr ("The current report database is not saved.\nSave it to some file with 'Save As', before saving it as waiver DB.")));
}
rdb->write (rdb->filename () + ".w");
END_PROTECTED
}
void
MarkerBrowserDialog::saveas_clicked ()
{
@ -385,6 +481,9 @@ BEGIN_PROTECTED
rdb->save (fn);
rdb->reset_modified ();
// update the RDB title strings
rdbs_changed ();
}
}
@ -394,6 +493,42 @@ BEGIN_PROTECTED
END_PROTECTED
}
void
MarkerBrowserDialog::info_clicked ()
{
rdb::Database *rdb = 0;
if (m_rdb_index < int (view ()->num_rdbs ()) && m_rdb_index >= 0) {
rdb = view ()->get_rdb (m_rdb_index);
}
if (! rdb) {
return;
}
std::string html;
html = "<html><body>\n";
html += "<h2>" + tl::escaped_to_html (rdb->name ()) + "</h2>\n";
if (! rdb->description ().empty ()) {
html += "<b>" + tl::to_string (tr ("Description: ")) + "</b>" + tl::escaped_to_html (tl::escape_string (rdb->description ())) + "<br/>\n";
}
if (! rdb->filename ().empty ()) {
html += "<b>" + tl::to_string (tr ("File: ")) + "</b>" + tl::escaped_to_html (rdb->filename ()) + "<br/>\n";
}
if (! rdb->original_file ().empty ()) {
html += "<b>" + tl::to_string (tr ("Original File: ")) + "</b>" + tl::escaped_to_html (rdb->original_file ()) + "<br/>\n";
}
if (! rdb->top_cell_name ().empty ()) {
html += "<b>" + tl::to_string (tr ("Top Cell: ")) + "</b>" + tl::escaped_to_html (rdb->top_cell_name ()) + "<br/>\n";
}
if (! rdb->generator ().empty ()) {
html += "<b>" + tl::to_string (tr ("Generator: ")) + "</b>" + tl::escaped_to_html (rdb->generator ()) + "<br/>\n";
}
html += "</body></html>";
std::unique_ptr<lay::BrowserDialog> info_dialog (new lay::BrowserDialog (this, html));
info_dialog->setWindowTitle (QObject::tr ("Marker Database Info"));
info_dialog->exec ();
}
void
MarkerBrowserDialog::reload_clicked ()
{
@ -439,6 +574,7 @@ BEGIN_PROTECTED
int rdb_index = view ()->add_rdb (db.release ());
mp_ui->rdb_cb->setCurrentIndex (rdb_index);
// it looks like the setCurrentIndex does not issue this signal:
rdb_index_changed (rdb_index);
@ -592,9 +728,13 @@ MarkerBrowserDialog::rdbs_changed ()
std::string text = rdb->name ();
if (! rdb->description ().empty ()) {
text += " (";
text += rdb->description ();
text += tl::escape_string (rdb->description ());
text += ")";
}
if (! rdb->filename ().empty () && rdb->name () != rdb->filename ()) {
text += " - ";
text += rdb->filename ();
}
mp_ui->rdb_cb->addItem (tl::to_qstring (text));
if (rdb->name () == m_rdb_name) {
rdb_index = i;
@ -688,11 +828,15 @@ MarkerBrowserDialog::update_content ()
mp_ui->central_stack->setCurrentIndex (1);
}
m_saveas_action->setEnabled (rdb != 0);
m_export_action->setEnabled (rdb != 0);
m_unload_action->setEnabled (rdb != 0);
m_unload_all_action->setEnabled (rdb != 0);
m_reload_action->setEnabled (rdb != 0);
mp_ui->save_action->setEnabled (rdb != 0);
mp_ui->saveas_action->setEnabled (rdb != 0);
mp_ui->saveas_waiver_db_action->setEnabled (rdb != 0);
mp_ui->apply_waiver_db_action->setEnabled (rdb != 0);
mp_ui->export_action->setEnabled (rdb != 0);
mp_ui->unload_action->setEnabled (rdb != 0);
mp_ui->unload_all_action->setEnabled (rdb != 0);
mp_ui->reload_action->setEnabled (rdb != 0);
mp_ui->info_action->setEnabled (rdb != 0);
mp_ui->browser_frame->enable_updates (false); // Avoid building the internal lists several times ...
mp_ui->browser_frame->set_rdb (0); // force update

View File

@ -74,7 +74,11 @@ private:
public slots:
void cv_index_changed (int);
void rdb_index_changed (int);
void info_clicked ();
void save_clicked ();
void saveas_clicked ();
void saveas_waiver_db_clicked ();
void apply_waiver_db_clicked ();
void export_clicked ();
void reload_clicked ();
void open_clicked ();
@ -98,12 +102,6 @@ private:
std::string m_rdb_name;
int m_rdb_index;
std::string m_open_filename;
QAction *m_open_action;
QAction *m_saveas_action;
QAction *m_export_action;
QAction *m_unload_action;
QAction *m_unload_all_action;
QAction *m_reload_action;
void update_content ();
void scan_layer ();

View File

@ -39,6 +39,7 @@
#include <QMessageBox>
#include <QHeaderView>
#include <QKeyEvent>
#include <QInputDialog>
namespace rdb
{
@ -103,13 +104,13 @@ class MarkerBrowserTreeViewModelCacheEntry
{
public:
MarkerBrowserTreeViewModelCacheEntry ()
: mp_parent (0), m_id (0), m_row (0), m_count (0)
: mp_parent (0), m_id (0), m_row (0), m_count (0), m_waived_count (0)
{
// .. nothing yet ..
}
MarkerBrowserTreeViewModelCacheEntry (rdb::id_type id, unsigned int branch)
: mp_parent (0), m_id ((id << 3) + (branch << 1)), m_row (0), m_count (0)
: mp_parent (0), m_id ((id << 3) + (branch << 1)), m_row (0), m_count (0), m_waived_count (0)
{
// .. nothing yet ..
}
@ -233,6 +234,25 @@ public:
m_count = c;
}
size_t waived_count () const
{
return m_waived_count;
}
void set_waived_count (size_t c)
{
m_waived_count = c;
}
void waive_or_unwaive (bool w)
{
if (w) {
++m_waived_count;
} else {
--m_waived_count;
}
}
void sort_by_key_name (bool ascending, const rdb::Database *database)
{
std::sort (m_ids.begin (), m_ids.end (), SortByKeyCompareFunc (ascending, database));
@ -257,7 +277,7 @@ private:
MarkerBrowserTreeViewModelCacheEntry *mp_parent;
rdb::id_type m_id;
unsigned int m_row;
size_t m_count;
size_t m_count, m_waived_count;
std::vector<MarkerBrowserTreeViewModelCacheEntry *> m_ids;
};
@ -267,7 +287,7 @@ SortByKeyCompareFunc::operator() (MarkerBrowserTreeViewModelCacheEntry *a, Marke
const rdb::Cell *ca = mp_rdb->cell_by_id (a->id ());
const rdb::Cell *cb = mp_rdb->cell_by_id (b->id ());
if (ca && cb) {
return m_ascending ? ca->name () < cb->name () : cb->name () < ca->name ();
return m_ascending ? ca->qname () < cb->qname () : cb->qname () < ca->qname ();
}
const rdb::Category *xa = mp_rdb->category_by_id (a->id ());
@ -338,14 +358,16 @@ public:
};
MarkerBrowserTreeViewModel ()
: mp_database (0), m_show_empty_ones (true)
: mp_database (0), m_show_empty_ones (true), m_waived_tag_id (0)
{
// .. nothing yet ..
// .. nothing yet ..
}
void set_database (const rdb::Database *db)
{
mp_database = db;
m_waived_tag_id = mp_database ? mp_database->tags ().tag ("waived").id () : 0;
invalidate ();
}
@ -384,6 +406,23 @@ public:
}
}
void waived_changed (const rdb::Item *item, bool waived)
{
const rdb::Category *cat = mp_database->category_by_id (item->category_id ());
while (cat) {
waive_or_unwaive (0, cat->id (), waived);
if (item->cell_id () != 0) {
waive_or_unwaive (item->cell_id (), cat->id (), waived);
}
cat = cat->parent ();
}
waive_or_unwaive (0, 0, waived);
if (item->cell_id () != 0) {
waive_or_unwaive (item->cell_id (), 0, waived);
}
}
int columnCount (const QModelIndex & /*parent*/) const
{
return 2;
@ -395,7 +434,7 @@ public:
if (section == 0) {
return QVariant (QObject::tr ("Cell / Category"));
} else if (section == 1) {
return QVariant (QObject::tr ("Count (Not Visited)"));
return QVariant (QObject::tr ("Count (Not Visited) - Waived"));
}
}
@ -436,13 +475,14 @@ public:
return true;
}
bool no_errors (const QModelIndex &index) const
bool no_errors (const QModelIndex &index, bool include_waived = false) const
{
MarkerBrowserTreeViewModelCacheEntry *node = (MarkerBrowserTreeViewModelCacheEntry *)(index.internalPointer ());
if (node && mp_database) {
rdb::id_type id = node->id ();
bool none = false;
size_t thr = include_waived ? node->waived_count () : 0;
const rdb::Cell *cell = mp_database->cell_by_id (id);
const rdb::Category *category = mp_database->category_by_id (id);
@ -464,13 +504,13 @@ public:
}
if (cell == 0 && category == 0) {
none = (mp_database->num_items () == 0);
none = (mp_database->num_items () <= thr);
} else if (category == 0) {
none = (cell->num_items () == 0);
none = (cell->num_items () <= thr);
} else if (cell == 0) {
none = (category->num_items () == 0);
none = (category->num_items () <= thr);
} else {
none = (mp_database->num_items (cell->id (), category->id ()) == 0);
none = (mp_database->num_items (cell->id (), category->id ()) <= thr);
}
return none;
@ -494,17 +534,31 @@ public:
if (index.column () == 1) {
std::string s;
if (node->count () > 0) {
size_t visited = node->visited_count (mp_database);
size_t waived = node->waived_count ();
if (visited < node->count ()) {
return QVariant (tl::to_qstring (tl::sprintf (tl::to_string (QObject::tr ("%lu (%lu)")), node->count (), node->count () - visited)));
s = tl::sprintf (tl::to_string (tr ("%lu (%lu)")), node->count (), node->count () - visited);
} else {
return QVariant ((unsigned int) node->count ());
s = tl::sprintf (tl::to_string (tr ("%lu")), node->count ());
}
} else {
return QVariant (QString::fromUtf8 (""));
if (waived > 0) {
if (waived == node->count ()) {
s += tl::to_string (tr (" - all waived"));
} else {
s += tl::sprintf (tl::to_string (tr (" - %lu")), waived);
}
}
}
return QVariant (tl::to_qstring (s));
} else if (index.column () == 0) {
rdb::id_type id = node->id ();
@ -529,7 +583,7 @@ public:
if (cell->name ().empty ()) {
return QObject::tr ("All Cells");
} else {
return QVariant (QString::fromUtf8 ("[") + tl::to_qstring (cell->name ()) + QString::fromUtf8 ("]"));
return QVariant (QString::fromUtf8 ("[") + tl::to_qstring (cell->qname ()) + QString::fromUtf8 ("]"));
}
}
@ -567,7 +621,7 @@ public:
}
// Green color if no errors are present
if (no_errors (index)) {
if (no_errors (index, true)) {
return QVariant (QColor (0, 192, 0));
}
@ -725,22 +779,103 @@ public:
private:
const rdb::Database *mp_database;
mutable MarkerBrowserTreeViewModelCacheEntry m_cache;
mutable std::multimap<std::pair<rdb::id_type, rdb::id_type>, MarkerBrowserTreeViewModelCacheEntry *> m_cache_by_ids;
bool m_show_empty_ones;
id_type m_waived_tag_id;
void waive_or_unwaive (rdb::id_type cell_id, rdb::id_type cat_id, bool waived)
{
auto k = std::make_pair (cell_id, cat_id);
auto c = m_cache_by_ids.find (k);
while (c != m_cache_by_ids.end () && c->first == k) {
c->second->waive_or_unwaive (waived);
++c;
}
}
size_t num_waived () const
{
size_t n = 0;
for (auto i = mp_database->items ().begin (); i != mp_database->items ().end (); ++i) {
if (i->has_tag (m_waived_tag_id)) {
++n;
}
}
return n;
}
size_t num_waived_per_cat (id_type cat_id) const
{
size_t n = 0;
auto ii = mp_database->items_by_category (cat_id);
for (auto i = ii.first; i != ii.second; ++i) {
if ((*i)->has_tag (m_waived_tag_id)) {
++n;
}
}
// include sub-categories
const rdb::Category *cat = mp_database->category_by_id (cat_id);
tl_assert (cat != 0);
for (auto c = cat->sub_categories ().begin (); c != cat->sub_categories ().end (); ++c) {
n += num_waived_per_cat (c->id ());
}
return n;
}
size_t num_waived_per_cell_and_cat (id_type cell_id, id_type cat_id) const
{
size_t n = 0;
auto ii = mp_database->items_by_cell_and_category (cell_id, cat_id);
for (auto i = ii.first; i != ii.second; ++i) {
if ((*i)->has_tag (m_waived_tag_id)) {
++n;
}
}
// include sub-categories
const rdb::Category *cat = mp_database->category_by_id (cat_id);
tl_assert (cat != 0);
for (auto c = cat->sub_categories ().begin (); c != cat->sub_categories ().end (); ++c) {
n += num_waived_per_cell_and_cat (cell_id, c->id ());
}
return n;
}
size_t num_waived_per_cell (id_type cell_id) const
{
auto ii = mp_database->items_by_cell (cell_id);
size_t n = 0;
for (auto i = ii.first; i != ii.second; ++i) {
if ((*i)->has_tag (m_waived_tag_id)) {
++n;
}
}
return n;
}
void invalidate ()
{
beginResetModel ();
m_cache.clear ();
m_cache_by_ids.clear ();
MarkerBrowserTreeViewModelCacheEntry *by_cell_node = new MarkerBrowserTreeViewModelCacheEntry(0, 0);
m_cache.add_child (by_cell_node);
m_cache_by_ids.insert (std::make_pair (std::make_pair (rdb::id_type (0), rdb::id_type (0)), by_cell_node));
MarkerBrowserTreeViewModelCacheEntry *by_category_node = new MarkerBrowserTreeViewModelCacheEntry(0, 1);
m_cache.add_child (by_category_node);
m_cache_by_ids.insert (std::make_pair (std::make_pair (rdb::id_type (0), rdb::id_type (0)), by_category_node));
MarkerBrowserTreeViewModelCacheEntry *all_node = new MarkerBrowserTreeViewModelCacheEntry(0, 2);
m_cache.add_child (all_node);
m_cache_by_ids.insert (std::make_pair (std::make_pair (rdb::id_type (0), rdb::id_type (0)), all_node));
m_cache.set_cache_valid (true);
@ -760,18 +895,22 @@ private:
{
const rdb::Category *category = mp_database->category_by_id (node->id ());
if (category) {
for (rdb::Categories::const_iterator c = category->sub_categories ().begin (); c != category->sub_categories ().end (); ++c) {
node->set_cache_valid (true);
MarkerBrowserTreeViewModelCacheEntry *child = new MarkerBrowserTreeViewModelCacheEntry (c->id (), node->branch ());
m_cache_by_ids.insert (std::make_pair (std::make_pair (rdb::id_type (0), c->id ()), child));
node->add_child (child);
child->set_count (mp_database->category_by_id (c->id ())->num_items ());
child->set_waived_count (num_waived_per_cat (c->id ()));
add_sub_categories (child);
}
}
}
@ -781,19 +920,24 @@ private:
const rdb::Category *category = mp_database->category_by_id (node->id ());
if (category) {
for (rdb::Categories::const_iterator c = category->sub_categories ().begin (); c != category->sub_categories ().end (); ++c) {
if (partial_tree.find (c->id ()) != partial_tree.end ()) {
MarkerBrowserTreeViewModelCacheEntry *child = new MarkerBrowserTreeViewModelCacheEntry (c->id (), node->branch ());
m_cache_by_ids.insert (std::make_pair (std::make_pair (cell_id, c->id ()), child));
node->add_child (child);
size_t n = mp_database->num_items (cell_id, c->id ());
child->set_count (n);
child->set_count (mp_database->num_items (cell_id, c->id ()));
child->set_waived_count (num_waived_per_cell_and_cat (cell_id, c->id ()));
add_sub_categories (cell_id, child, partial_tree);
}
}
}
}
@ -813,27 +957,44 @@ private:
if (branch == 0) {
for (rdb::Database::const_cell_iterator c = mp_database->cells ().begin (); c != mp_database->cells ().end (); ++c) {
if (mp_database->cell_by_id (c->id ()) && (m_show_empty_ones || mp_database->cell_by_id (c->id ())->num_items () != 0)) {
MarkerBrowserTreeViewModelCacheEntry *child = new MarkerBrowserTreeViewModelCacheEntry (c->id (), branch);
m_cache_by_ids.insert (std::make_pair (std::make_pair (c->id (), rdb::id_type (0)), child));
child->set_count (mp_database->cell_by_id (c->id ())->num_items ());
child->set_waived_count (num_waived_per_cell (c->id ()));
node->add_child (child);
}
}
} else if (branch == 1) {
for (rdb::Categories::const_iterator c = mp_database->categories ().begin (); c != mp_database->categories ().end (); ++c) {
if (mp_database->category_by_id (c->id ()) && (m_show_empty_ones || mp_database->category_by_id (c->id ())->num_items () != 0)) {
MarkerBrowserTreeViewModelCacheEntry *child = new MarkerBrowserTreeViewModelCacheEntry (c->id (), branch);
m_cache_by_ids.insert (std::make_pair (std::make_pair (rdb::id_type (0), c->id ()), child));
child->set_count (mp_database->category_by_id (c->id ())->num_items ());
child->set_waived_count (num_waived_per_cat (c->id ()));
node->add_child (child);
add_sub_categories (child);
}
}
}
node->set_count (mp_database->num_items ());
node->set_waived_count (num_waived ());
} else if (branch == 0) {
@ -869,9 +1030,14 @@ private:
MarkerBrowserTreeViewModelCacheEntry *child = new MarkerBrowserTreeViewModelCacheEntry (c->id (), branch);
size_t n = mp_database->num_items (id, c->id ());
if (m_show_empty_ones || n != 0) {
m_cache_by_ids.insert (std::make_pair (std::make_pair (id, c->id ()), child));
child->set_count (n);
child->set_waived_count (num_waived_per_cell_and_cat (id, c->id ()));
node->add_child (child);
add_sub_categories (id, child, category_ids);
@ -903,8 +1069,14 @@ private:
size_t n = mp_database->num_items (*c, id);
if (m_show_empty_ones || n != 0) {
m_cache_by_ids.insert (std::make_pair (std::make_pair (*c, id), child));
child->set_count (n);
child->set_waived_count (num_waived_per_cell_and_cat (*c, id));
node->add_child (child);
} else {
delete child;
}
@ -996,6 +1168,16 @@ public:
m_sorting_order = sorting_order;
}
int sorting () const
{
return m_sorting;
}
bool sorting_order () const
{
return m_sorting_order;
}
template <class Iter>
bool set_items (const std::vector <std::pair<Iter, Iter> > &be_vector, size_t max_marker_count)
{
@ -1528,6 +1710,7 @@ MarkerBrowserPage::MarkerBrowserPage (QWidget * /*parent*/)
connect (list_down_pb, SIGNAL (clicked ()), this, SLOT (list_down_clicked ()));
connect (flags_pb, SIGNAL (clicked ()), this, SLOT (flag_button_clicked ()));
connect (important_pb, SIGNAL (clicked ()), this, SLOT (important_button_clicked ()));
connect (edit_pb, SIGNAL (clicked ()), this, SLOT (edit_button_clicked ()));
connect (waive_pb, SIGNAL (clicked ()), this, SLOT (waived_button_clicked ()));
connect (photo_pb, SIGNAL (clicked ()), this, SLOT (snapshot_button_clicked ()));
connect (nophoto_pb, SIGNAL (clicked ()), this, SLOT (remove_snapshot_button_clicked ()));
@ -1724,6 +1907,16 @@ MarkerBrowserPage::set_rdb (rdb::Database *database)
rerun_button->setToolTip (QString ());
}
// mark items visited that carry the waived flag
if (mp_database) {
id_type waived_tag_id = mp_database->tags ().tag ("waived").id ();
for (auto i = mp_database->items ().begin (); i != mp_database->items ().end (); ++i) {
if (i->has_tag (waived_tag_id)) {
mp_database->set_item_visited (i.operator-> (), true);
}
}
}
QAbstractItemModel *tree_model = directory_tree->model ();
MarkerBrowserTreeViewModel *new_model = new MarkerBrowserTreeViewModel ();
@ -1745,6 +1938,9 @@ MarkerBrowserPage::set_rdb (rdb::Database *database)
QAbstractItemModel *list_model = markers_list->model ();
MarkerBrowserListViewModel *new_list_model = new MarkerBrowserListViewModel ();
// default sorting is by waived flag
new_list_model->set_sorting (2, true);
markers_list->header ()->setSortIndicator (new_list_model->sorting (), new_list_model->sorting_order () ? Qt::AscendingOrder : Qt::DescendingOrder);
new_list_model->set_database (database);
markers_list->setModel (new_list_model);
connect (markers_list->selectionModel (), SIGNAL (selectionChanged (const QItemSelection &, const QItemSelection &)), this, SLOT (markers_selection_changed (const QItemSelection &, const QItemSelection &)));
@ -1939,6 +2135,8 @@ MarkerBrowserPage::update_info_text ()
size_t n_category = 0;
const rdb::Item *item = 0;
size_t n_item = 0;
std::string comment;
size_t n_comment = 0;
for (QModelIndexList::const_iterator selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) {
@ -1950,6 +2148,11 @@ MarkerBrowserPage::update_info_text ()
item = i;
++n_item;
if (! item->comment ().empty () && item->comment () != comment) {
comment = item->comment ();
++n_comment;
}
const rdb::Cell *c = mp_database->cell_by_id (item->cell_id ());
if (c && c != cell) {
cell = c;
@ -1976,11 +2179,11 @@ MarkerBrowserPage::update_info_text ()
info += "<h3>";
if (category && n_category == 1) {
info += category->name ();
tl::escape_to_html (info, category->name ());
}
if (cell && n_cell == 1 && ! cell->name ().empty ()) {
info += " [" + cell->name () + "]";
tl::escape_to_html (info, std::string (" [") + cell->name () + "]");
}
info += "</h3>";
@ -1997,6 +2200,12 @@ MarkerBrowserPage::update_info_text ()
info += "</p>";
}
if (! comment.empty () && n_comment == 1) {
info += "<p style=\"color:gray\">";
tl::escape_to_html (info, comment);
info += "</p>";
}
info += "<p/>";
if (item && n_item == 1) {
@ -2085,8 +2294,6 @@ MarkerBrowserPage::do_update_markers ()
item_index = size_t (selected_item->row ());
++n_item;
std::string info;
const rdb::Cell *c = mp_database->cell_by_id (item->cell_id ());
if (c && c != cell) {
cell = c;
@ -2736,7 +2943,61 @@ MarkerBrowserPage::flag_menu_selected ()
}
}
void
void
MarkerBrowserPage::edit_button_clicked ()
{
if (! mp_database) {
return;
}
MarkerBrowserListViewModel *list_model = dynamic_cast<MarkerBrowserListViewModel *> (markers_list->model ());
if (! list_model) {
return;
}
std::string str;
QModelIndexList selected = markers_list->selectionModel ()->selectedIndexes ();
for (auto selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) {
if (selected_item->column () == 0) {
const rdb::Item *i = list_model->item (selected_item->row ());
if (! i->comment ().empty ()) {
if (str.empty ()) {
str = i->comment ();
} else if (str != i->comment ()) {
str.clear ();
break;
}
}
}
}
bool ok = false;
#if QT_VERSION >= 0x50200
QString new_text = QInputDialog::getMultiLineText (this, QObject::tr ("Edit Marker Comment"), QObject::tr ("Comment"), tl::to_qstring (str), &ok);
str = tl::to_string (new_text);
#else
QString new_text = QInputDialog::getText (this, QObject::tr ("Edit Marker Comment"), QObject::tr ("Comment"), QLineEdit::Normal, tl::to_qstring (tl::escape_string (str)), &ok);
str = tl::unescape_string (tl::to_string (new_text));
#endif
if (ok) {
QModelIndexList selected = markers_list->selectionModel ()->selectedIndexes ();
for (auto selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) {
if (selected_item->column () == 0) {
const rdb::Item *i = list_model->item (selected_item->row ());
mp_database->set_item_comment (i, str);
}
}
update_info_text ();
}
}
void
MarkerBrowserPage::waived_button_clicked ()
{
if (! mp_database) {
@ -2754,7 +3015,7 @@ MarkerBrowserPage::waived_button_clicked ()
size_t nno = 0;
QModelIndexList selected = markers_list->selectionModel ()->selectedIndexes ();
for (QModelIndexList::const_iterator selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) {
for (auto selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) {
if (selected_item->column () == 0) {
const rdb::Item *i = list_model->item (selected_item->row ());
if (i) {
@ -2792,7 +3053,7 @@ MarkerBrowserPage::important_button_clicked ()
size_t nno = 0;
QModelIndexList selected = markers_list->selectionModel ()->selectedIndexes ();
for (QModelIndexList::const_iterator selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) {
for (auto selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) {
if (selected_item->column () == 0) {
const rdb::Item *i = list_model->item (selected_item->row ());
if (i) {
@ -2831,7 +3092,7 @@ MarkerBrowserPage::remove_snapshot_button_clicked ()
if (msgbox.exec () == QMessageBox::Yes) {
QModelIndexList selected = markers_list->selectionModel ()->selectedIndexes ();
for (QModelIndexList::const_iterator selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) {
for (auto selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) {
if (selected_item->column () == 0) {
const rdb::Item *i = list_model->item (selected_item->row ());
if (i) {
@ -2858,7 +3119,7 @@ MarkerBrowserPage::snapshot_button_clicked ()
}
QModelIndexList selected = markers_list->selectionModel ()->selectedIndexes ();
for (QModelIndexList::const_iterator selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) {
for (auto selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) {
const rdb::Item *i = list_model->item (selected_item->row ());
if (i) {
@ -2903,7 +3164,12 @@ MarkerBrowserPage::unwaive_all ()
return;
}
QMessageBox msgbox (QMessageBox::Question,
MarkerBrowserTreeViewModel *tree_model = dynamic_cast<MarkerBrowserTreeViewModel *> (directory_tree->model ());
if (! tree_model) {
return;
}
QMessageBox msgbox (QMessageBox::Question,
QObject::tr ("Remove All Waived"),
QObject::tr ("Are you sure to remove the waived flags from all markers?"),
QMessageBox::Yes | QMessageBox::No);
@ -2912,7 +3178,10 @@ MarkerBrowserPage::unwaive_all ()
id_type waived_tag_id = mp_database->tags ().tag ("waived").id ();
for (Items::const_iterator i = mp_database->items ().begin (); i != mp_database->items ().end (); ++i) {
mp_database->remove_item_tag (&*i, waived_tag_id);
if (i->has_tag (waived_tag_id)) {
mp_database->remove_item_tag (i.operator-> (), waived_tag_id);
tree_model->waived_changed (i.operator-> (), false);
}
}
list_model->mark_data_changed ();
@ -2933,7 +3202,7 @@ MarkerBrowserPage::revisit_all ()
}
for (Items::const_iterator i = mp_database->items ().begin (); i != mp_database->items ().end (); ++i) {
mp_database->set_item_visited (&*i, false);
mp_database->set_item_visited (i.operator-> (), false);
}
list_model->mark_data_changed ();
@ -2960,7 +3229,7 @@ MarkerBrowserPage::revisit_non_waived ()
for (Items::const_iterator i = mp_database->items ().begin (); i != mp_database->items ().end (); ++i) {
if (! i->has_tag (waived_tag_id)) {
mp_database->set_item_visited (&*i, false);
mp_database->set_item_visited (i.operator-> (), false);
}
}
@ -2988,7 +3257,7 @@ MarkerBrowserPage::revisit_important ()
for (Items::const_iterator i = mp_database->items ().begin (); i != mp_database->items ().end (); ++i) {
if (i->has_tag (important_tag_id)) {
mp_database->set_item_visited (&*i, false);
mp_database->set_item_visited (i.operator-> (), false);
}
}
@ -3015,7 +3284,7 @@ MarkerBrowserPage::mark_important ()
id_type important_tag_id = mp_database->tags ().tag ("important").id ();
QModelIndexList selected = markers_list->selectionModel ()->selectedIndexes ();
for (QModelIndexList::const_iterator selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) {
for (auto selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) {
if (selected_item->column () == 0) {
const rdb::Item *i = list_model->item (selected_item->row ());
if (i) {
@ -3042,7 +3311,7 @@ MarkerBrowserPage::mark_unimportant ()
id_type important_tag_id = mp_database->tags ().tag ("important").id ();
QModelIndexList selected = markers_list->selectionModel ()->selectedIndexes ();
for (QModelIndexList::const_iterator selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) {
for (auto selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) {
if (selected_item->column () == 0) {
const rdb::Item *i = list_model->item (selected_item->row ());
if (i) {
@ -3079,7 +3348,7 @@ MarkerBrowserPage::mark_visited (bool f)
}
QModelIndexList selected = markers_list->selectionModel ()->selectedIndexes ();
for (QModelIndexList::const_iterator selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) {
for (auto selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) {
if (selected_item->column () == 0) {
const rdb::Item *i = list_model->item (selected_item->row ());
if (i) {
@ -3099,32 +3368,17 @@ MarkerBrowserPage::mark_visited (bool f)
void
MarkerBrowserPage::waive ()
{
if (! mp_database) {
return;
}
MarkerBrowserListViewModel *list_model = dynamic_cast<MarkerBrowserListViewModel *> (markers_list->model ());
if (! list_model) {
return;
}
id_type waived_tag_id = mp_database->tags ().tag ("waived").id ();
QModelIndexList selected = markers_list->selectionModel ()->selectedIndexes ();
for (QModelIndexList::const_iterator selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) {
if (selected_item->column () == 0) {
const rdb::Item *i = list_model->item (selected_item->row ());
if (i) {
mp_database->add_item_tag (i, waived_tag_id);
}
}
}
list_model->mark_data_changed ();
waive_or_unwaive (true);
}
void
void
MarkerBrowserPage::unwaive ()
{
waive_or_unwaive (false);
}
void
MarkerBrowserPage::waive_or_unwaive (bool w)
{
if (! mp_database) {
return;
@ -3135,19 +3389,38 @@ MarkerBrowserPage::unwaive ()
return;
}
MarkerBrowserTreeViewModel *tree_model = dynamic_cast<MarkerBrowserTreeViewModel *> (directory_tree->model ());
if (! tree_model) {
return;
}
id_type waived_tag_id = mp_database->tags ().tag ("waived").id ();
QModelIndexList selected = markers_list->selectionModel ()->selectedIndexes ();
for (QModelIndexList::const_iterator selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) {
for (auto selected_item = selected.begin (); selected_item != selected.end (); ++selected_item) {
if (selected_item->column () == 0) {
const rdb::Item *i = list_model->item (selected_item->row ());
if (i) {
mp_database->remove_item_tag (i, waived_tag_id);
bool was_waived = i->has_tag (waived_tag_id);
if (w != was_waived) {
if (w) {
mp_database->add_item_tag (i, waived_tag_id);
} else {
mp_database->remove_item_tag (i, waived_tag_id);
}
if (w) {
// waiving an item makes it visited (rationale: once waived, an item is no
// longer of interest)
mp_database->set_item_visited (i, true);
}
tree_model->waived_changed (i, w);
}
}
}
}
list_model->mark_data_changed ();
tree_model->mark_data_changed ();
}
void

View File

@ -179,6 +179,7 @@ public slots:
void flag_menu_selected ();
void important_button_clicked ();
void waived_button_clicked ();
void edit_button_clicked ();
void snapshot_button_clicked ();
void remove_snapshot_button_clicked ();
void revisit_non_waived ();
@ -238,6 +239,7 @@ private:
void do_update_markers ();
void update_info_text ();
void rerun_macro ();
void waive_or_unwaive (bool w);
};
} // namespace rdb

View File

@ -305,6 +305,8 @@ TEST(40_DeviceExtractorErrors)
TEST(50_BasicSoftConnection)
{
run_test (_this, "soft_connect1", "soft_connect1.gds", true, false /*no LVS*/);
// issue #1691
run_test (_this, "soft_connect1a", "soft_connect1.gds", true, false /*no LVS*/);
}
// No errors

View File

@ -265,17 +265,25 @@ Class<rdb::Cell> decl_RdbCell ("rdb", "RdbCell",
"The cell name is an string that identifies the category in the database. "
"Additionally, a cell may carry a variant identifier which is a string that uniquely identifies a cell "
"in the context of its variants. The \"qualified name\" contains both the cell name and the variant name. "
"Cell names are also used to identify report database cell's with layout cells. "
"Cell names are also used to identify report database cells with layout cells. For variants, the layout cell name "
"can be specified explicitly with the \\layout_name attribute (see \\RdbDatabase#create_cell). The latter is available "
"since version 0.29.1.\n"
"@return The cell name\n"
) +
gsi::method ("variant", &rdb::Cell::variant,
"@brief Gets the cell variant name\n"
"A variant name additionally identifies the cell when multiple cells with the same name are present. "
"A variant name is either assigned automatically or set when creating a cell. "
"A variant name is either assigned automatically or set when creating a cell.\n"
"@return The cell variant name\n"
) +
gsi::method ("qname", &rdb::Cell::qname,
"@brief Gets the cell's qualified name\n"
gsi::method ("layout_name", &rdb::Cell::layout_name,
"@brief Gets the name of the layout cell\n"
"For variants, this string is the name of the actual layout cell. If empty, the cell is assume to be called 'name'.\n"
"@return The layout cell name\n"
"This read-only attribute has been added in version 0.29.1.\n"
) +
gsi::method ("qname", &rdb::Cell::qname,
"@brief Gets the qualified name of the cell\n"
"The qualified name is a combination of the cell name and optionally the variant name. "
"It is used to identify the cell by name in a unique way.\n"
"@return The qualified name\n"
@ -911,6 +919,10 @@ Class<rdb::Item> decl_RdbItem ("rdb", "RdbItem",
"@brief Remove the tag with the given id from the item\n"
"If a tag with that ID does not exists on this item, this method does nothing."
) +
gsi::method ("remove_tags", &rdb::Item::remove_tags,
"@brief Removes all tags from the item\n"
"This method has been introduced in version 0.29.1."
) +
gsi::method ("has_tag?", &rdb::Item::has_tag, gsi::arg ("tag_id"),
"@brief Returns a value indicating whether the item has a tag with the given ID\n"
"@return True, if the item has a tag with the given ID\n"
@ -928,6 +940,19 @@ Class<rdb::Item> decl_RdbItem ("rdb", "RdbItem",
"See \\image_str how to obtain the image.\n\n"
"This method has been introduced in version 0.28.\n"
) +
gsi::method ("comment", &rdb::Item::comment,
"@brief Gets the common associated with this item as a string\n"
"@return The comment string\n"
"The comment string is an arbitrary string added by the user to the item.\n"
"\n"
"This attribute has been added in version 0.29.1.\n"
) +
gsi::method ("comment=", &rdb::Item::set_comment, gsi::arg ("comment"),
"@brief Sets the common associated with this item as a string\n"
"See \\comment for a description of that attribute.\n"
"\n"
"This attribute has been added in version 0.29.1.\n"
) +
gsi::method ("image_str", &rdb::Item::image_str,
"@brief Gets the image associated with this item as a string\n"
"@return A base64-encoded image file (in PNG format)\n"
@ -1317,6 +1342,7 @@ Class<rdb::Database> decl_ReportDatabase ("rdb", "ReportDatabase",
"@brief Creates a new sub-category\n"
"@param parent The category under which the category should be created\n"
"@param name The name of the category\n"
"Since version 0.29.1, 'parent' can be nil. In that case, a top-level category is created."
) +
gsi::method ("category_by_path", &rdb::Database::category_by_name, gsi::arg ("path"),
"@brief Gets a category by path\n"
@ -1344,10 +1370,12 @@ Class<rdb::Database> decl_ReportDatabase ("rdb", "ReportDatabase",
"@brief Creates a new cell\n"
"@param name The name of the cell\n"
) +
gsi::method ("create_cell", (rdb::Cell *(rdb::Database::*) (const std::string &, const std::string &)) &rdb::Database::create_cell, gsi::arg ("name"), gsi::arg ("variant"),
gsi::method ("create_cell", (rdb::Cell *(rdb::Database::*) (const std::string &, const std::string &, const std::string &)) &rdb::Database::create_cell, gsi::arg ("name"), gsi::arg ("variant"), gsi::arg ("layout_name", std::string ()),
"@brief Creates a new cell, potentially as a variant for a cell with the same name\n"
"@param name The name of the cell\n"
"@param variant The variant name of the cell\n"
"@param layout_name For variants, this is the name of the layout cell. If empty, 'name' is used for the layout cell name.\n"
"The 'layout_name' argument has been added in version 0.29.1.\n"
) +
gsi::method ("variants", &rdb::Database::variants, gsi::arg ("name"),
"@brief Gets the variants for a given cell name\n"
@ -1562,6 +1590,36 @@ Class<rdb::Database> decl_ReportDatabase ("rdb", "ReportDatabase",
"@param trans The transformation to apply\n"
"@param edge_pairs The list of edge_pairs for which the items are created\n"
) +
gsi::method ("apply", &rdb::Database::apply, gsi::arg ("other"),
"@brief Transfers item attributes from one database to another for identical items\n"
"This method will identify items that are identical between the two databases and transfer "
"item attributes from the 'other' database to this database. Transferable attributes are:\n"
"\n"
"@ul\n"
"@li Images @/li\n"
"@li Item tags @/li\n"
"@/ul\n"
"\n"
"Existing attributes in this database are overwritten.\n"
"\n"
"Items are identical if\n"
"\n"
"@ul\n"
"@li They belong to the same cell (by qname) @/li\n"
"@li They belong to the same category (by name) @/li\n"
"@li Their values are identical @/li\n"
"@/ul\n"
"\n"
"Values are identical if their individual values and (optional) value tags are identical. "
"Values tagged with a tag unknown to the other database are ignored. "
"The order of values matters during the compare. So the value pair (17.0, 'abc') is different from ('abc', 17.0).\n"
"\n"
"The intended application for this method is use for error waiving: as the waived attribute is a transferable "
"attribute, it is possible to apply the waived flag from from a waiver database (the 'other' database) using this "
"method.\n"
"\n"
"This method has been added in version 0.29.1."
) +
gsi::method ("is_modified?", &rdb::Database::is_modified,
"@brief Returns a value indicating whether the database has been modified\n"
) +

View File

@ -60,16 +60,18 @@ namespace rdb
// type index specializations
template <> RDB_PUBLIC int type_index_of<double> () { return 0; }
template <> RDB_PUBLIC int type_index_of<std::string> () { return 1; }
template <> RDB_PUBLIC int type_index_of<db::DPolygon> () { return 2; }
template <> RDB_PUBLIC int type_index_of<db::DEdge> () { return 3; }
template <> RDB_PUBLIC int type_index_of<db::DEdgePair> () { return 4; }
template <> RDB_PUBLIC int type_index_of<db::DBox> () { return 5; }
template <> RDB_PUBLIC int type_index_of<db::DPath> () { return 6; }
template <> RDB_PUBLIC int type_index_of<db::DText> () { return 7; }
template <> RDB_PUBLIC int type_index_of<int> () { return 1; }
template <> RDB_PUBLIC int type_index_of<std::string> () { return 2; }
template <> RDB_PUBLIC int type_index_of<db::DPolygon> () { return 3; }
template <> RDB_PUBLIC int type_index_of<db::DEdge> () { return 4; }
template <> RDB_PUBLIC int type_index_of<db::DEdgePair> () { return 5; }
template <> RDB_PUBLIC int type_index_of<db::DBox> () { return 6; }
template <> RDB_PUBLIC int type_index_of<db::DPath> () { return 7; }
template <> RDB_PUBLIC int type_index_of<db::DText> () { return 8; }
// Explicit instantiations to make VC++ happy in debug mode
template class RDB_PUBLIC Value<double>;
template class RDB_PUBLIC Value<int>;
template class RDB_PUBLIC Value<std::string>;
template class RDB_PUBLIC Value<db::DPolygon>;
template class RDB_PUBLIC Value<db::DEdge>;
@ -85,6 +87,11 @@ template <> RDB_PUBLIC std::string Value<double>::to_string () const
return "float: " + tl::to_string (m_value);
}
template <> RDB_PUBLIC std::string Value<int>::to_string () const
{
return "int: " + tl::to_string (m_value);
}
template <> RDB_PUBLIC std::string Value<std::string>::to_string () const
{
return "text: " + tl::to_word_or_quoted_string (m_value);
@ -127,6 +134,11 @@ template <> RDB_PUBLIC std::string Value<double>::to_display_string () const
return tl::to_string (m_value);
}
template <> RDB_PUBLIC std::string Value<int>::to_display_string () const
{
return tl::to_string (m_value);
}
template <> RDB_PUBLIC std::string Value<std::string>::to_display_string () const
{
return m_value;
@ -162,6 +174,62 @@ template <> RDB_PUBLIC std::string Value<db::DText>::to_display_string () const
return to_string ();
}
// compare implementations
template <> RDB_PUBLIC bool Value<double>::compare (const ValueBase *o) const
{
const Value<double> *other = static_cast<const Value<double> *> (o);
return m_value < other->m_value - db::epsilon;
}
template <> RDB_PUBLIC bool Value<int>::compare (const ValueBase *o) const
{
const Value<int> *other = static_cast<const Value<int> *> (o);
return m_value < other->m_value;
}
template <> RDB_PUBLIC bool Value<std::string>::compare (const ValueBase *o) const
{
const Value<std::string> *other = static_cast<const Value<std::string> *> (o);
return m_value < other->m_value;
}
template <> RDB_PUBLIC bool Value<db::DPolygon>::compare (const ValueBase *o) const
{
const Value<db::DPolygon> *other = static_cast<const Value<db::DPolygon> *> (o);
return m_value.less (other->m_value);
}
template <> RDB_PUBLIC bool Value<db::DEdge>::compare (const ValueBase *o) const
{
const Value<db::DEdge> *other = static_cast<const Value<db::DEdge> *> (o);
return m_value.less (other->m_value);
}
template <> RDB_PUBLIC bool Value<db::DEdgePair>::compare (const ValueBase *o) const
{
const Value<db::DEdgePair> *other = static_cast<const Value<db::DEdgePair> *> (o);
return m_value.less (other->m_value);
}
template <> RDB_PUBLIC bool Value<db::DBox>::compare (const ValueBase *o) const
{
const Value<db::DBox> *other = static_cast<const Value<db::DBox> *> (o);
return m_value.less (other->m_value);
}
template <> RDB_PUBLIC bool Value<db::DPath>::compare (const ValueBase *o) const
{
const Value<db::DPath> *other = static_cast<const Value<db::DPath> *> (o);
return m_value.less (other->m_value);
}
template <> RDB_PUBLIC bool Value<db::DText>::compare (const ValueBase *o) const
{
const Value<db::DText> *other = static_cast<const Value<db::DText> *> (o);
return m_value.less (other->m_value);
}
// is_shape implementations
template <> RDB_PUBLIC bool Value<double>::is_shape () const
@ -169,6 +237,11 @@ template <> RDB_PUBLIC bool Value<double>::is_shape () const
return false;
}
template <> RDB_PUBLIC bool Value<int>::is_shape () const
{
return false;
}
template <> RDB_PUBLIC bool Value<std::string>::is_shape () const
{
return false;
@ -403,6 +476,58 @@ Values::operator= (const Values &d)
return *this;
}
bool
Values::compare (const Values &other, const std::map<id_type, id_type> &tag_map, const std::map<id_type, id_type> &rev_tag_map) const
{
Values::const_iterator a = begin (), b = other.begin ();
while (a != end () && b != other.end ()) {
id_type t12 = 0;
while (a != end () && a->tag_id () != 0) {
auto j = tag_map.find (a->tag_id ());
if (j != tag_map.end ()) {
t12 = j->second;
break;
}
++a;
}
id_type t2 = 0;
while (b != other.end () && b->tag_id () != 0) {
auto j = rev_tag_map.find (b->tag_id ());
if (j != rev_tag_map.end ()) {
t2 = j->first;
break;
}
++b;
}
if (a == end () || b == other.end ()) {
return b != other.end ();
}
if (t12 != t2) {
return t12 < t2;
}
if (a->get () && b->get ()) {
if (rdb::ValueBase::compare (a->get (), b->get ())) {
return true;
} else if (rdb::ValueBase::compare (b->get (), a->get ())) {
return false;
}
} else if ((a->get () != 0) != (b->get () != 0)) {
return (a->get () != 0) < (b->get () != 0);
}
++a;
++b;
}
return b != other.end ();
}
std::string
Values::to_string (const Database *rdb) const
{
@ -447,7 +572,7 @@ Cells::import_cell (const Cell &c)
{
Cell *cell;
if (mp_database) {
cell = mp_database->create_cell (c.name (), c.variant ());
cell = mp_database->create_cell (c.name (), c.variant (), c.layout_name ());
} else {
cell = new Cell (0, c.name ());
add_cell (cell);
@ -479,8 +604,8 @@ Cell::Cell (id_type id, const std::string &name)
// .. nothing yet ..
}
Cell::Cell (id_type id, const std::string &name, const std::string &variant)
: m_id (id), m_name (name), m_variant (variant), m_num_items (0), m_num_items_visited (0), mp_database (0)
Cell::Cell (id_type id, const std::string &name, const std::string &variant, const std::string &layout_name = std::string ())
: m_id (id), m_name (name), m_variant (variant), m_layout_name (layout_name), m_num_items (0), m_num_items_visited (0), mp_database (0)
{
// .. nothing yet ..
}
@ -812,14 +937,14 @@ Tags::tag (const std::string &name, bool user_tag)
const Tag &
Tags::tag (id_type id) const
{
tl_assert (id - 1 < m_tags.size () && id > 0);
tl_assert (id < m_tags.size () + 1 && id > 0);
return m_tags [id - 1];
}
Tag &
Tags::tag (id_type id)
{
tl_assert (id - 1 < m_tags.size () && id > 0);
tl_assert (id < m_tags.size () + 1 && id > 0);
return m_tags [id - 1];
}
@ -870,6 +995,7 @@ Item &Item::operator= (const Item &d)
m_category_id = d.m_category_id;
m_visited = d.m_visited;
m_multiplicity = d.m_multiplicity;
m_comment = d.m_comment;
m_tag_ids = d.m_tag_ids;
m_image_str = d.m_image_str;
}
@ -1205,6 +1331,10 @@ Database::import_cells (const Cells &cells)
Category *
Database::create_category (Category *parent, const std::string &name)
{
if (! parent) {
return create_category (name);
}
set_modified ();
Category *cat = create_category (&parent->sub_categories (), name);
@ -1252,7 +1382,7 @@ Database::category_by_id_non_const (id_type id)
}
Cell *
Database::create_cell (const std::string &name, const std::string &variant)
Database::create_cell (const std::string &name, const std::string &variant, const std::string &layout_name)
{
set_modified ();
@ -1289,13 +1419,13 @@ Database::create_cell (const std::string &name, const std::string &variant)
}
}
new_cell = new Cell (++m_next_id, name, tl::to_string (variant_index + 1));
new_cell = new Cell (++m_next_id, name, tl::to_string (variant_index + 1), layout_name);
variant->second.push_back (new_cell->id ());
} else {
new_cell = new Cell (++m_next_id, name);
new_cell = new Cell (++m_next_id, name, std::string (), layout_name);
}
@ -1305,7 +1435,7 @@ Database::create_cell (const std::string &name, const std::string &variant)
} else {
new_cell = new Cell (++m_next_id, name, variant);
new_cell = new Cell (++m_next_id, name, variant, layout_name);
m_cells.add_cell (new_cell);
m_cells_by_id.insert (std::make_pair (new_cell->id (), new_cell));
m_cells_by_qname.insert (std::make_pair (new_cell->qname (), new_cell));
@ -1399,6 +1529,13 @@ Database::remove_item_tag (const Item *item, id_type tag)
const_cast <Item *> (item)->remove_tag (tag);
}
void
Database::set_item_comment (const Item *item, const std::string &comment)
{
set_modified ();
const_cast <Item *> (item)->set_comment (comment);
}
#if defined(HAVE_QT)
void
Database::set_item_image (const Item *item, const QImage &image)
@ -1625,7 +1762,7 @@ read_db_from_layout (rdb::Database *db, tl::InputStream &is)
}
void
Database::load (const std::string &fn)
Database::load (std::string fn)
{
tl::log << "Loading RDB from " << fn;
@ -1659,6 +1796,161 @@ Database::load (const std::string &fn)
}
}
namespace
{
class ValueMapEntryCompare
{
public:
ValueMapEntryCompare (const std::map<id_type, id_type> &tag2tag, const std::map<id_type, id_type> &rev_tag2tag)
{
mp_tag2tag = &tag2tag;
mp_rev_tag2tag = &rev_tag2tag;
}
bool operator() (const Item *a, const Item *b) const
{
return a->values ().compare (b->values (), *mp_tag2tag, *mp_rev_tag2tag);
}
private:
const std::map<id_type, id_type> *mp_tag2tag;
const std::map<id_type, id_type> *mp_rev_tag2tag;
};
class ValueMapEntry
{
public:
ValueMapEntry ()
: mp_tag2tag (0), mp_rev_tag2tag (0)
{ }
void build (const rdb::Database &rdb, id_type cell_id, id_type cat_id, const std::map<id_type, id_type> &tag2tag, const std::map<id_type, id_type> &rev_tag2tag)
{
mp_tag2tag = &tag2tag;
mp_rev_tag2tag = &rev_tag2tag;
auto i2i = rdb.items_by_cell_and_category (cell_id, cat_id);
size_t n = 0;
for (auto i = i2i.first; i != i2i.second; ++i) {
++n;
}
m_items.reserve (n);
for (auto i = i2i.first; i != i2i.second; ++i) {
m_items.push_back ((*i).operator-> ());
}
ValueMapEntryCompare cmp (*mp_tag2tag, *mp_rev_tag2tag);
std::sort (m_items.begin (), m_items.end (), cmp);
}
const Item *find (const rdb::Item &item) const
{
ValueMapEntryCompare cmp (*mp_tag2tag, *mp_rev_tag2tag);
auto i = std::lower_bound (m_items.begin (), m_items.end (), &item, cmp);
if (i == m_items.end ()) {
return 0;
}
if (cmp (&item, *i) || cmp (*i, &item)) {
return 0;
} else {
return *i;
}
}
public:
std::vector<const Item *> m_items;
const std::map<id_type, id_type> *mp_tag2tag;
const std::map<id_type, id_type> *mp_rev_tag2tag;
};
}
static void map_category (const rdb::Category &cat, const rdb::Database &db, std::map<id_type, id_type> &cat2cat)
{
const rdb::Category *this_cat = db.category_by_name (cat.path ());
if (this_cat) {
cat2cat.insert (std::make_pair (this_cat->id (), cat.id ()));
}
for (auto c = cat.sub_categories ().begin (); c != cat.sub_categories ().end (); ++c) {
map_category (*c, db, cat2cat);
}
}
void
Database::apply (const rdb::Database &other)
{
std::map<id_type, id_type> cell2cell;
std::map<id_type, id_type> cat2cat;
std::map<id_type, id_type> tag2tag;
std::map<id_type, id_type> rev_tag2tag;
for (auto c = other.cells ().begin (); c != other.cells ().end (); ++c) {
// TODO: do we have a consistent scheme of naming variants? What requirements
// exist towards detecting variant specific waivers
const rdb::Cell *this_cell = cell_by_qname (c->qname ());
if (this_cell) {
cell2cell.insert (std::make_pair (this_cell->id (), c->id ()));
}
}
for (auto c = other.categories ().begin (); c != other.categories ().end (); ++c) {
map_category (*c, *this, cat2cat);
}
std::map<std::string, id_type> tags_by_name;
for (auto c = tags ().begin_tags (); c != tags ().end_tags (); ++c) {
tags_by_name.insert (std::make_pair (c->name (), c->id ()));
}
for (auto c = other.tags ().begin_tags (); c != other.tags ().end_tags (); ++c) {
auto t = tags_by_name.find (c->name ());
if (t != tags_by_name.end ()) {
tag2tag.insert (std::make_pair (t->second, c->id ()));
rev_tag2tag.insert (std::make_pair (c->id (), t->second));
}
}
std::map<std::pair<id_type, id_type>, ValueMapEntry> value_map;
for (Items::iterator i = items_non_const ().begin (); i != items_non_const ().end (); ++i) {
auto icell = cell2cell.find (i->cell_id ());
if (icell == cell2cell.end ()) {
continue;
}
auto icat = cat2cat.find (i->category_id ());
if (icat == cat2cat.end ()) {
continue;
}
// build a cache of value vs. value
auto vmap = value_map.find (std::make_pair (icell->second, icat->second));
if (vmap == value_map.end ()) {
vmap = value_map.insert (std::make_pair (std::make_pair (icell->second, icat->second), ValueMapEntry ())).first;
vmap->second.build (other, icell->second, icat->second, tag2tag, rev_tag2tag);
}
// find a value in the reference DB
const rdb::Item *other = vmap->second.find (*i);
if (other) {
// actually transfer the attributes here
i->set_comment (other->comment ());
// TODO: this has some optimization potential in terms of performance ...
i->set_image_str (other->image_str ());
i->set_tag_str (other->tag_str ());
}
}
}
void
Database::scan_layout (const db::Layout &layout, db::cell_index_type cell_index, const std::vector<std::pair<unsigned int, std::string> > &layers_and_descriptions, bool flat)
{

View File

@ -506,10 +506,7 @@ public:
return type_index_of<C> ();
}
bool compare (const ValueBase *other) const
{
return m_value < static_cast<const Value<C> *> (other)->m_value;
}
bool compare (const ValueBase *other) const;
bool is_shape () const;
@ -679,6 +676,19 @@ public:
*/
Values &operator= (const Values &d);
/**
* @brief Compare two value sets (less operator)
*
* This compare function will use the tag mapping provided by tag map ("this" tag id to "other" tag id).
* Values with tags not listed in the tag map will not be compared.
* Untagged values (tag_id 0) will be compared always.
*
* "rev_tag_map" needs to be the reverse of "tag_map".
*
* The order of the values matters.
*/
bool compare (const Values &other, const std::map<id_type, id_type> &tag_map, const std::map<id_type, id_type> &rev_tag_map) const;
/**
* @brief The const iterator (begin)
*/
@ -739,6 +749,14 @@ public:
m_values.swap (other.m_values);
}
/**
* @brief Clears the values
*/
void clear ()
{
m_values.clear ();
}
/**
* @brief Convert the values collection to a string
*/
@ -993,6 +1011,26 @@ public:
*/
void set_image_str (const std::string &s);
/**
* @brief Gets the item comment
*
* The comment string is an arbitrary string attached to the item.
*/
const std::string &comment () const
{
return m_comment;
}
/**
* @brief Sets the item comment
*
* The comment string is an arbitrary string attached to the item.
*/
void set_comment (const std::string &s)
{
m_comment = s;
}
/**
* @brief Get the database reference
*/
@ -1017,6 +1055,7 @@ private:
id_type m_cell_id;
id_type m_category_id;
size_t m_multiplicity;
std::string m_comment;
bool m_visited;
std::vector <bool> m_tag_ids;
Database *mp_database;
@ -1441,7 +1480,7 @@ public:
*
* This method is provided for persistency application only. It should not be used otherwise.
*/
Cell (id_type id, const std::string &name, const std::string &variant);
Cell (id_type id, const std::string &name, const std::string &variant, const std::string &layout_name);
/**
* @brief Cell destructor
@ -1467,7 +1506,7 @@ public:
}
/**
* @brief Get the cell name
* @brief Gets the cell name
*/
const std::string &name () const
{
@ -1475,7 +1514,7 @@ public:
}
/**
* @brief Set the name string (setter)
* @brief Sets the name string
*
* This method must not be used for items in the database to keep the database consistent.
*/
@ -1485,7 +1524,7 @@ public:
}
/**
* @brief Get the variant id
* @brief Gets the variant id
*/
const std::string &variant () const
{
@ -1493,7 +1532,7 @@ public:
}
/**
* @brief Set the variant string (setter)
* @brief Sets the variant string
*
* This method must not be used for items in the database to keep the database consistent.
*/
@ -1502,6 +1541,24 @@ public:
m_variant = v;
}
/**
* @brief Gets the layout cell name
*/
const std::string &layout_name () const
{
return m_layout_name;
}
/**
* @brief Sets the layout cell string
*
* This method must not be used for items in the database to keep the database consistent.
*/
void set_layout_name (const std::string &s)
{
m_layout_name = s;
}
/**
* @brief Get the qualified name (name plus optionally the variant id separated by a colon)
*/
@ -1579,6 +1636,7 @@ private:
id_type m_id;
std::string m_name;
std::string m_variant;
std::string m_layout_name;
size_t m_num_items;
size_t m_num_items_visited;
References m_references;
@ -2182,7 +2240,7 @@ public:
*/
Cell *create_cell (const std::string &name)
{
return create_cell (name, std::string ());
return create_cell (name, std::string (), std::string ());
}
/**
@ -2191,8 +2249,11 @@ public:
* A cell with name name/variant combination must not exist already.
* If the variant string is empty, this method behaves the same as the
* method without variant.
*
* "layout_name" is the name of the cell in the layout. If empty, the layout
* cell is assumed to be identical to "name".
*/
Cell *create_cell (const std::string &name, const std::string &variant);
Cell *create_cell (const std::string &name, const std::string &variant, const std::string &layout_name);
/**
* @brief Get all variants registered for a given cell name (not qname!)
@ -2286,6 +2347,11 @@ public:
*/
void remove_item_tag (const Item *item, id_type tag);
/**
* @brief Sets the comment string of the item
*/
void set_item_comment (const Item *item, const std::string &comment);
#if defined(HAVE_QT)
/**
* @brief Set the image of an item
@ -2378,12 +2444,29 @@ public:
*/
void save (const std::string &filename);
/**
* @brief Write the database to a file
*
* This function is like "save", but does not update the file name attribute.
*/
void write (const std::string &filename);
/**
* @brief Load the database from a file
*
* @brief This clears the existing database.
* Note: This clears the existing database.
* The argument intentionally is a copy, so we can call
* "load (this->filename ())" for reloading.
*/
void load (const std::string &filename);
void load (std::string filename);
/**
* @brief Applies the attributes from a different database
*
* Attributes are waived flags, images etc.
* The attributes are applied to markers with identical value(s), category and cell context.
*/
void apply (const rdb::Database &other);
/**
* @brief Scans a layout into this RDB

View File

@ -91,7 +91,8 @@ make_rdb_structure (rdb::Database *rdb)
tl::make_element_with_parent_ref<rdb::Cell, rdb::Cells::const_iterator, rdb::Cells> (&rdb::Cells::begin, &rdb::Cells::end, &rdb::Cells::import_cell, "cell",
tl::make_member<std::string, rdb::Cell> (&rdb::Cell::name, &rdb::Cell::set_name, "name") +
tl::make_member<std::string, rdb::Cell> (&rdb::Cell::variant, &rdb::Cell::set_variant, "variant") +
tl::make_element_with_parent_ref<rdb::References, rdb::Cell> (&rdb::Cell::references, &rdb::Cell::import_references, "references",
tl::make_member<std::string, rdb::Cell> (&rdb::Cell::layout_name, &rdb::Cell::set_layout_name, "layout-name") +
tl::make_element_with_parent_ref<rdb::References, rdb::Cell> (&rdb::Cell::references, &rdb::Cell::import_references, "references",
tl::make_element_with_parent_ref<rdb::Reference, rdb::References::const_iterator, rdb::References> (&rdb::References::begin, &rdb::References::end, &rdb::References::insert, "ref",
tl::make_member<std::string, rdb::Reference> (&rdb::Reference::parent_cell_qname, &rdb::Reference::set_parent_cell_qname, "parent") +
tl::make_member<std::string, rdb::Reference> (&rdb::Reference::trans_str, &rdb::Reference::set_trans_str, "trans")
@ -106,6 +107,7 @@ make_rdb_structure (rdb::Database *rdb)
tl::make_member<std::string, rdb::Item> (&rdb::Item::cell_qname, &rdb::Item::set_cell_qname, "cell") +
tl::make_member<bool, rdb::Item> (&rdb::Item::visited, &rdb::Item::set_visited, "visited") +
tl::make_member<size_t, rdb::Item> (&rdb::Item::multiplicity, &rdb::Item::set_multiplicity, "multiplicity") +
tl::make_member<std::string, rdb::Item> (&rdb::Item::comment, &rdb::Item::set_comment, "comment") +
tl::make_member<std::string, rdb::Item> (&rdb::Item::image_str, &rdb::Item::set_image_str, "image") +
tl::make_element<rdb::Values, rdb::Item> (&rdb::Item::values, &rdb::Item::set_values, "values",
tl::make_member<rdb::ValueWrapper, rdb::Values::const_iterator, rdb::Values> (&rdb::Values::begin, &rdb::Values::end, &rdb::Values::add, "value", ValueConverter (rdb))
@ -116,17 +118,25 @@ make_rdb_structure (rdb::Database *rdb)
}
// -------------------------------------------------------------
// Implementation of rdb::Database::save
// Implementation of rdb::Database::save and write
// TODO: move this somewhere else - with generalized functionality
void
rdb::Database::save (const std::string &fn)
{
tl::OutputStream os (fn, tl::OutputStream::OM_Auto);
make_rdb_structure (this).write (os, *this);
write (fn);
set_filename (fn);
}
tl::log << "Saved RDB to " << fn;
void
rdb::Database::write (const std::string &fn)
{
tl::OutputStream os (fn, tl::OutputStream::OM_Auto);
make_rdb_structure (this).write (os, *this);
if (tl::verbosity () >= 10) {
tl::log << "Saved RDB to " << fn;
}
}
// -------------------------------------------------------------

View File

@ -174,10 +174,28 @@ public:
const rdb::Cell *cell_for_id (const db::Layout *layout, db::cell_index_type ci)
{
tl_assert (layout != 0);
std::string cn = layout->cell_name (ci);
const rdb::Cell *rdb_cell = mp_rdb->cell_by_qname (cn);
std::string layout_cn = cn;
std::string qname = cn;
std::string var;
// resolve references to original cells in deep mode and determine variant
if (layout->builder ()) {
const db::Layout *source = layout->builder ()->source ().layout ();
if (source) {
const std::pair<db::cell_index_type, std::string> &vs = layout->builder ()->variant_of_source (ci);
if (! vs.second.empty () && source->is_valid_cell_index (vs.first)) {
var = vs.second;
cn = source->cell_name (vs.first);
qname = cn + ":" + var;
}
}
}
const rdb::Cell *rdb_cell = mp_rdb->cell_by_qname (qname);
if (! rdb_cell) {
return mp_rdb->create_cell (cn);
return mp_rdb->create_cell (cn, var, layout_cn);
} else {
return rdb_cell;
}

View File

@ -69,15 +69,15 @@ void run_rve_test (tl::TestBase *_this, const std::string &fn_rve, const std::st
TEST(1)
{
run_rve_test (_this, "rve1.db", "rve1_au.txt");
run_rve_test (_this, "rve1.db", "rve1_au_2.txt");
}
TEST(2)
{
run_rve_test (_this, "rve2.db", "rve2_au.txt");
run_rve_test (_this, "rve2.db", "rve2_au_2.txt");
}
TEST(3)
{
run_rve_test (_this, "rve3.db", "rve3_au.txt");
run_rve_test (_this, "rve3.db", "rve3_au_2.txt");
}

View File

@ -521,11 +521,11 @@ TEST(6)
EXPECT_EQ (db.variants ("c1")[0], c1->id ());
EXPECT_EQ (db.variants ("c1")[1], c1a->id ());
rdb::Cell *c1b = db.create_cell ("c1", "var");
rdb::Cell *c1b = db.create_cell ("c1", "var", std::string ());
EXPECT_EQ (c1b->qname (), "c1:var")
EXPECT_EQ (db.variants ("c1").size (), size_t (3));
rdb::Cell *c2 = db.create_cell ("c2", "1027");
rdb::Cell *c2 = db.create_cell ("c2", "1027", std::string ());
EXPECT_EQ (c2->qname (), "c2:1027");
EXPECT_EQ (db.variants ("c2").size (), size_t (1));
@ -534,8 +534,9 @@ TEST(6)
EXPECT_EQ (c2->qname (), "c2:1027")
EXPECT_EQ (db.variants ("c2").size (), size_t (2));
rdb::Cell *c2b = db.create_cell ("c2", "var");
rdb::Cell *c2b = db.create_cell ("c2", "var", "c2$1");
EXPECT_EQ (c2b->qname (), "c2:var")
EXPECT_EQ (c2b->layout_name (), "c2$1")
rdb::Cell *c2c = db.create_cell ("c2");
EXPECT_EQ (c2c->qname (), "c2:2");
@ -598,3 +599,226 @@ TEST(7)
#endif
}
TEST(8_ApplyBasicEmptyValue)
{
rdb::Database db1;
rdb::Category *cat1 = db1.create_category ("cat_name");
rdb::Cell *c1 = db1.create_cell ("cell");
rdb::Item *i1 = db1.create_item (c1->id (), cat1->id ());
rdb::Database db2;
db2.create_category ("dummy_cat");
rdb::Category *cat2 = db2.create_category ("cat_name");
db2.create_cell ("dummy_cell");
rdb::Cell *c2 = db2.create_cell ("cell");
rdb::Item *i2 = db2.create_item (c2->id (), cat2->id ());
rdb::id_type tag2 = db2.tags ().tag ("tag2").id ();
i2->add_tag (tag2);
EXPECT_EQ (i2->tag_str (), "tag2");
EXPECT_EQ (i1->tag_str (), "");
// empty value apply
db1.apply (db2);
EXPECT_EQ (i1->tag_str (), "tag2");
}
TEST(9_ApplyBasicSomeValue)
{
rdb::Database db1;
rdb::Category *cat1 = db1.create_category ("cat_name");
rdb::Cell *c1 = db1.create_cell ("cell");
rdb::Item *i1 = db1.create_item (c1->id (), cat1->id ());
i1->add_value (std::string ("abc"));
rdb::Database db2;
db2.create_category ("dummy_cat");
rdb::Category *cat2 = db2.create_category ("cat_name");
db2.create_cell ("dummy_cell");
rdb::Cell *c2 = db2.create_cell ("cell");
rdb::Item *i2 = db2.create_item (c2->id (), cat2->id ());
db2.tags ().tag ("dummy_tag");
rdb::id_type tag2 = db2.tags ().tag ("tag2").id ();
i2->add_tag (tag2);
EXPECT_EQ (i2->tag_str (), "tag2");
EXPECT_EQ (i1->tag_str (), "");
// empty value apply
db1.apply (db2);
// not applied (different value)
EXPECT_EQ (i1->tag_str (), "");
// incorrect value
i2->add_value (17);
db1.apply (db2);
// still not applied
EXPECT_EQ (i1->tag_str (), "");
// correct value
i2->values ().clear ();
i2->add_value (std::string ("abc"));
db1.apply (db2);
// now, the tag is applied
EXPECT_EQ (i1->tag_str (), "tag2");
// too many values
i1->remove_tags ();
i2->add_value (17);
db1.apply (db2);
// not applied
EXPECT_EQ (i1->tag_str (), "");
}
TEST(10_ApplyTaggedValue)
{
rdb::Database db1;
rdb::Category *cat1 = db1.create_category ("cat_name");
rdb::Cell *c1 = db1.create_cell ("cell");
rdb::Item *i1 = db1.create_item (c1->id (), cat1->id ());
rdb::id_type vtag11 = db1.tags ().tag ("vtag1").id ();
rdb::id_type vtag12 = db1.tags ().tag ("vtag2").id ();
i1->add_value (std::string ("abc"));
rdb::Database db2;
db2.create_category ("dummy_cat");
rdb::Category *cat2 = db2.create_category ("cat_name");
db2.create_cell ("dummy_cell");
rdb::Cell *c2 = db2.create_cell ("cell");
rdb::Item *i2 = db2.create_item (c2->id (), cat2->id ());
db2.tags ().tag ("dummy_tag");
rdb::id_type tag2 = db2.tags ().tag ("tag2").id ();
rdb::id_type vtag21 = db2.tags ().tag ("vtag1").id ();
i2->add_tag (tag2);
i2->add_value (std::string ("abc"), vtag21);
// empty tag vs. vtag1
db1.apply (db2);
// not applied (empty tag vs. tagged)
EXPECT_EQ (i1->tag_str (), "");
// vtag2 vs. vtag1
i1->values ().clear ();
i1->add_value (std::string ("abc"), vtag12);
db1.apply (db2);
// not applied (different tags)
EXPECT_EQ (i1->tag_str (), "");
// vtag1 vs. vtag1
i1->values ().clear ();
i1->add_value (std::string ("abc"), vtag11);
db1.apply (db2);
// this time it is applied (same tag)
EXPECT_EQ (i1->tag_str (), "tag2");
}
TEST(11_ApplyWrongCat)
{
rdb::Database db1;
rdb::Category *cat1 = db1.create_category ("cat_name");
rdb::Cell *c1 = db1.create_cell ("cell");
rdb::Item *i1 = db1.create_item (c1->id (), cat1->id ());
rdb::Database db2;
db2.create_category ("dummy_cat");
rdb::Category *cat2 = db2.create_category ("xcat_name");
db2.create_cell ("dummy_cell");
rdb::Cell *c2 = db2.create_cell ("cell");
rdb::Item *i2 = db2.create_item (c2->id (), cat2->id ());
rdb::id_type tag2 = db2.tags ().tag ("tag2").id ();
i2->add_tag (tag2);
EXPECT_EQ (i2->tag_str (), "tag2");
EXPECT_EQ (i1->tag_str (), "");
// empty value apply
db1.apply (db2);
EXPECT_EQ (i1->tag_str (), "");
}
TEST(12_ApplyWrongCell)
{
rdb::Database db1;
rdb::Category *cat1 = db1.create_category ("cat_name");
rdb::Cell *c1 = db1.create_cell ("cell");
rdb::Item *i1 = db1.create_item (c1->id (), cat1->id ());
rdb::Database db2;
db2.create_category ("dummy_cat");
rdb::Category *cat2 = db2.create_category ("cat_name");
db2.create_cell ("dummy_cell");
rdb::Cell *c2 = db2.create_cell ("xcell");
rdb::Item *i2 = db2.create_item (c2->id (), cat2->id ());
rdb::id_type tag2 = db2.tags ().tag ("tag2").id ();
i2->add_tag (tag2);
EXPECT_EQ (i2->tag_str (), "tag2");
EXPECT_EQ (i1->tag_str (), "");
// empty value apply
db1.apply (db2);
EXPECT_EQ (i1->tag_str (), "");
}
TEST(13_ApplyIgnoreUnknownTag)
{
rdb::Database db1;
rdb::Category *cat1 = db1.create_category ("cat_name");
rdb::Cell *c1 = db1.create_cell ("cell");
rdb::Item *i1 = db1.create_item (c1->id (), cat1->id ());
rdb::id_type vtag11 = db1.tags ().tag ("vtag1").id ();
i1->add_value (std::string ("abc"), vtag11);
rdb::Database db2;
db2.create_category ("dummy_cat");
rdb::Category *cat2 = db2.create_category ("cat_name");
db2.create_cell ("dummy_cell");
rdb::Cell *c2 = db2.create_cell ("cell");
rdb::Item *i2 = db2.create_item (c2->id (), cat2->id ());
db2.tags ().tag ("dummy_tag");
rdb::id_type tag2 = db2.tags ().tag ("tag2").id ();
rdb::id_type vtag21 = db2.tags ().tag ("vtag1").id ();
rdb::id_type vtag22 = db2.tags ().tag ("vtag2").id ();
i2->add_tag (tag2);
// same tags, different values
i2->add_value (std::string ("xyz"), vtag21);
db1.apply (db2);
// not applied
EXPECT_EQ (i1->tag_str (), "");
// different tags without mapping
i2->values ().clear ();
i2->add_value (std::string ("xyz"), vtag22);
// values with incompatible tags are ignored -> tag2 is applied
db1.apply (db2);
EXPECT_EQ (i1->tag_str (), "tag2");
}

View File

@ -145,14 +145,14 @@ ExpressionParserContext::where () const
// ----------------------------------------------------------------------------
// Utilities for evaluation
static double to_double (const ExpressionParserContext &context, const tl::Variant &v)
static double to_double (const ExpressionParserContext &context, const tl::Variant &v, unsigned int narg)
{
if (v.can_convert_to_double ()) {
return v.to_double ();
} else if (v.is_list ()) {
return v.get_list ().size ();
} else {
throw EvalError (tl::to_string (tr ("Double precision floating point value expected")), context);
throw EvalError (tl::to_string (tr ("Double precision floating point value expected for argument #")) + tl::to_string (narg + 1), context);
}
}
@ -162,50 +162,50 @@ static double to_double (const ExpressionParserContext &context, const std::vect
throw EvalError (tl::to_string (tr ("Function expects a single numeric argument")), context);
}
return to_double (context, v [0]);
return to_double (context, v [0], 0);
}
static long to_long (const ExpressionParserContext &context, const tl::Variant &v)
static long to_long (const ExpressionParserContext &context, const tl::Variant &v, int narg)
{
if (v.can_convert_to_long ()) {
return v.to_long ();
} else if (v.is_list ()) {
return long (v.get_list ().size ());
} else {
throw EvalError (tl::to_string (tr ("Integer value expected")), context);
throw EvalError (tl::to_string (tr ("Integer value expected for argument #")) + tl::to_string (narg + 1), context);
}
}
static unsigned long to_ulong (const ExpressionParserContext &context, const tl::Variant &v)
static unsigned long to_ulong (const ExpressionParserContext &context, const tl::Variant &v, int narg)
{
if (v.can_convert_to_ulong ()) {
return v.to_ulong ();
} else if (v.is_list ()) {
return (unsigned long) (v.get_list ().size ());
} else {
throw EvalError (tl::to_string (tr ("Unsigned integer value expected")), context);
throw EvalError (tl::to_string (tr ("Unsigned integer value expected for argument #")) + tl::to_string (narg + 1), context);
}
}
static long long to_longlong (const ExpressionParserContext &context, const tl::Variant &v)
static long long to_longlong (const ExpressionParserContext &context, const tl::Variant &v, int narg)
{
if (v.can_convert_to_longlong ()) {
return v.to_longlong ();
} else if (v.is_list ()) {
return long (v.get_list ().size ());
} else {
throw EvalError (tl::to_string (tr ("Integer value expected")), context);
throw EvalError (tl::to_string (tr ("Integer value expected for argument #")) + tl::to_string (narg + 1), context);
}
}
static unsigned long long to_ulonglong (const ExpressionParserContext &context, const tl::Variant &v)
static unsigned long long to_ulonglong (const ExpressionParserContext &context, const tl::Variant &v, int narg)
{
if (v.can_convert_to_ulonglong ()) {
return v.to_ulong ();
} else if (v.is_list ()) {
return (unsigned long long) (v.get_list ().size ());
} else {
throw EvalError (tl::to_string (tr ("Unsigned integer value expected")), context);
throw EvalError (tl::to_string (tr ("Unsigned integer value expected for argument #")) + tl::to_string (narg + 1), context);
}
}
@ -1089,13 +1089,13 @@ public:
v.swap (o);
} else if (v->is_longlong ()) {
v.set (tl::Variant (v->to_longlong () << to_longlong (m_context, *b)));
v.set (tl::Variant (v->to_longlong () << to_longlong (m_context, *b, 1)));
} else if (v->is_ulonglong ()) {
v.set (tl::Variant (v->to_ulonglong () << to_ulonglong (m_context, *b)));
v.set (tl::Variant (v->to_ulonglong () << to_ulonglong (m_context, *b, 1)));
} else if (v->is_ulong ()) {
v.set (tl::Variant (v->to_ulong () << to_ulong (m_context, *b)));
v.set (tl::Variant (v->to_ulong () << to_ulong (m_context, *b, 1)));
} else {
v.set (tl::Variant (to_long (m_context, *v) << to_long (m_context, *b)));
v.set (tl::Variant (to_long (m_context, *v, 0) << to_long (m_context, *b, 1)));
}
}
};
@ -1145,13 +1145,13 @@ public:
v.swap (o);
} else if (v->is_longlong ()) {
v.set (tl::Variant (v->to_longlong () >> to_longlong (m_context, *b)));
v.set (tl::Variant (v->to_longlong () >> to_longlong (m_context, *b, 1)));
} else if (v->is_ulonglong ()) {
v.set (tl::Variant (v->to_ulonglong () >> to_ulonglong (m_context, *b)));
v.set (tl::Variant (v->to_ulonglong () >> to_ulonglong (m_context, *b, 1)));
} else if (v->is_ulong ()) {
v.set (tl::Variant (v->to_ulong () >> to_ulong (m_context, *b)));
v.set (tl::Variant (v->to_ulong () >> to_ulong (m_context, *b, 1)));
} else {
v.set (tl::Variant (to_long (m_context, *v) >> to_long (m_context, *b)));
v.set (tl::Variant (to_long (m_context, *v, 0) >> to_long (m_context, *b, 1)));
}
}
};
@ -1203,17 +1203,17 @@ public:
} else if (v->is_a_string () || b->is_a_string ()) {
v.set (tl::Variant (std::string (v->to_string ()) + b->to_string ()));
} else if (v->is_double () || b->is_double ()) {
v.set (tl::Variant (to_double (m_context, *v) + to_double (m_context, *b)));
v.set (tl::Variant (to_double (m_context, *v, 0) + to_double (m_context, *b, 1)));
} else if (v->is_ulonglong () || b->is_ulonglong ()) {
v.set (tl::Variant (to_ulonglong (m_context, *v) + to_ulonglong (m_context, *b)));
v.set (tl::Variant (to_ulonglong (m_context, *v, 0) + to_ulonglong (m_context, *b, 1)));
} else if (v->is_longlong () || b->is_longlong ()) {
v.set (tl::Variant (to_longlong (m_context, *v) + to_longlong (m_context, *b)));
v.set (tl::Variant (to_longlong (m_context, *v, 0) + to_longlong (m_context, *b, 1)));
} else if (v->is_ulong () || b->is_ulong ()) {
v.set (tl::Variant (to_ulong (m_context, *v) + to_ulong (m_context, *b)));
v.set (tl::Variant (to_ulong (m_context, *v, 0) + to_ulong (m_context, *b, 1)));
} else if (v->is_long () || b->is_long ()) {
v.set (tl::Variant (to_long (m_context, *v) + to_long (m_context, *b)));
v.set (tl::Variant (to_long (m_context, *v, 0) + to_long (m_context, *b, 1)));
} else {
v.set (tl::Variant (to_double (m_context, *v) + to_double (m_context, *b)));
v.set (tl::Variant (to_double (m_context, *v, 0) + to_double (m_context, *b, 1)));
}
}
};
@ -1263,17 +1263,17 @@ public:
v.swap (o);
} else if (v->is_double () || b->is_double ()) {
v.set (tl::Variant (to_double (m_context, *v) - to_double (m_context, *b)));
v.set (tl::Variant (to_double (m_context, *v, 0) - to_double (m_context, *b, 1)));
} else if (v->is_ulonglong () || b->is_ulonglong ()) {
v.set (tl::Variant (to_ulonglong (m_context, *v) - to_ulonglong (m_context, *b)));
v.set (tl::Variant (to_ulonglong (m_context, *v, 0) - to_ulonglong (m_context, *b, 1)));
} else if (v->is_longlong () || b->is_longlong ()) {
v.set (tl::Variant (to_longlong (m_context, *v) - to_longlong (m_context, *b)));
v.set (tl::Variant (to_longlong (m_context, *v, 0) - to_longlong (m_context, *b, 1)));
} else if (v->is_ulong () || b->is_ulong ()) {
v.set (tl::Variant (to_ulong (m_context, *v) - to_ulong (m_context, *b)));
v.set (tl::Variant (to_ulong (m_context, *v, 0) - to_ulong (m_context, *b, 1)));
} else if (v->is_long () || b->is_long ()) {
v.set (tl::Variant (to_long (m_context, *v) - to_long (m_context, *b)));
v.set (tl::Variant (to_long (m_context, *v, 0) - to_long (m_context, *b, 1)));
} else {
v.set (tl::Variant (to_double (m_context, *v) - to_double (m_context, *b)));
v.set (tl::Variant (to_double (m_context, *v, 0) - to_double (m_context, *b, 1)));
}
}
};
@ -1324,7 +1324,7 @@ public:
} else if (v->is_a_string ()) {
long x = to_long (m_context, *b);
long x = to_long (m_context, *b, 1);
if (x < 0) {
throw EvalError (tl::to_string (tr ("Numeric argument of '*' operator with string must be positive")), m_context);
}
@ -1339,7 +1339,7 @@ public:
} else if (b->is_a_string ()) {
long x = to_long (m_context, *v);
long x = to_long (m_context, *v, 0);
if (x < 0) {
throw EvalError (tl::to_string (tr ("Numeric argument of '*' operator with string must be positive")), m_context);
}
@ -1353,17 +1353,17 @@ public:
v.set (tl::Variant (s));
} else if (v->is_double () || b->is_double ()) {
v.set (tl::Variant (to_double (m_context, *v) * to_double (m_context, *b)));
v.set (tl::Variant (to_double (m_context, *v, 0) * to_double (m_context, *b, 1)));
} else if (v->is_ulonglong () || b->is_ulonglong ()) {
v.set (tl::Variant (to_ulonglong (m_context, *v) * to_ulonglong (m_context, *b)));
v.set (tl::Variant (to_ulonglong (m_context, *v, 0) * to_ulonglong (m_context, *b, 1)));
} else if (v->is_longlong () || b->is_longlong ()) {
v.set (tl::Variant (to_longlong (m_context, *v) * to_longlong (m_context, *b)));
v.set (tl::Variant (to_longlong (m_context, *v, 0) * to_longlong (m_context, *b, 1)));
} else if (v->is_ulong () || b->is_ulong ()) {
v.set (tl::Variant (to_ulong (m_context, *v) * to_ulong (m_context, *b)));
v.set (tl::Variant (to_ulong (m_context, *v, 0) * to_ulong (m_context, *b, 1)));
} else if (v->is_long () || b->is_long ()) {
v.set (tl::Variant (to_long (m_context, *v) * to_long (m_context, *b)));
v.set (tl::Variant (to_long (m_context, *v, 0) * to_long (m_context, *b, 1)));
} else {
v.set (tl::Variant (to_double (m_context, *v) * to_double (m_context, *b)));
v.set (tl::Variant (to_double (m_context, *v, 0) * to_double (m_context, *b, 1)));
}
}
};
@ -1413,41 +1413,41 @@ public:
v.swap (o);
} else if (v->is_double () || b->is_double ()) {
double d = to_double (m_context, *b);
double d = to_double (m_context, *b, 1);
if (d == 0) {
throw EvalError (tl::to_string (tr ("Division by zero")), m_context);
}
v.set (tl::Variant (to_double (m_context, *v) / d));
v.set (tl::Variant (to_double (m_context, *v, 0) / d));
} else if (v->is_ulonglong () || b->is_ulonglong ()) {
unsigned long long d = to_ulonglong (m_context, *b);
unsigned long long d = to_ulonglong (m_context, *b, 1);
if (d == 0) {
throw EvalError (tl::to_string (tr ("Division by zero")), m_context);
}
v.set (tl::Variant (to_ulonglong (m_context, *v) / d));
v.set (tl::Variant (to_ulonglong (m_context, *v, 0) / d));
} else if (v->is_longlong () || b->is_longlong ()) {
long long d = to_longlong (m_context, *b);
long long d = to_longlong (m_context, *b, 1);
if (d == 0) {
throw EvalError (tl::to_string (tr ("Division by zero")), m_context);
}
v.set (tl::Variant (to_longlong (m_context, *v) / d));
v.set (tl::Variant (to_longlong (m_context, *v, 0) / d));
} else if (v->is_ulong () || b->is_ulong ()) {
unsigned long d = to_ulong (m_context, *b);
unsigned long d = to_ulong (m_context, *b, 1);
if (d == 0) {
throw EvalError (tl::to_string (tr ("Division by zero")), m_context);
}
v.set (tl::Variant (to_ulong (m_context, *v) / d));
v.set (tl::Variant (to_ulong (m_context, *v, 0) / d));
} else if (v->is_long () || b->is_long ()) {
long d = to_long (m_context, *b);
long d = to_long (m_context, *b, 1);
if (d == 0) {
throw EvalError (tl::to_string (tr ("Division by zero")), m_context);
}
v.set (tl::Variant (to_long (m_context, *v) / d));
v.set (tl::Variant (to_long (m_context, *v, 0) / d));
} else {
double d = to_double (m_context, *b);
double d = to_double (m_context, *b, 1);
if (d == 0) {
throw EvalError (tl::to_string (tr ("Division by zero")), m_context);
}
v.set (tl::Variant (to_double (m_context, *v) / d));
v.set (tl::Variant (to_double (m_context, *v, 0) / d));
}
}
};
@ -1497,29 +1497,29 @@ public:
v.swap (o);
} else if (v->is_ulonglong () || b->is_ulonglong ()) {
unsigned long long d = to_ulonglong (m_context, *b);
unsigned long long d = to_ulonglong (m_context, *b, 1);
if (d == 0) {
throw EvalError (tl::to_string (tr ("Modulo by zero")), m_context);
}
v.set (tl::Variant (to_ulonglong (m_context, *v) % d));
v.set (tl::Variant (to_ulonglong (m_context, *v, 0) % d));
} else if (v->is_longlong () || b->is_longlong ()) {
long long d = to_longlong (m_context, *b);
long long d = to_longlong (m_context, *b, 1);
if (d == 0) {
throw EvalError (tl::to_string (tr ("Modulo by zero")), m_context);
}
v.set (tl::Variant (to_longlong (m_context, *v) % d));
v.set (tl::Variant (to_longlong (m_context, *v, 0) % d));
} else if (v->is_ulong () || b->is_ulong ()) {
unsigned long d = to_ulong (m_context, *b);
unsigned long d = to_ulong (m_context, *b, 1);
if (d == 0) {
throw EvalError (tl::to_string (tr ("Modulo by zero")), m_context);
}
v.set (tl::Variant (to_ulong (m_context, *v) % d));
v.set (tl::Variant (to_ulong (m_context, *v, 0) % d));
} else {
long d = to_long (m_context, *b);
long d = to_long (m_context, *b, 1);
if (d == 0) {
throw EvalError (tl::to_string (tr ("Modulo by zero")), m_context);
}
v.set (tl::Variant (to_long (m_context, *v) % d));
v.set (tl::Variant (to_long (m_context, *v, 0) % d));
}
}
};
@ -1569,13 +1569,13 @@ public:
v.swap (o);
} else if (v->is_ulonglong () || b->is_ulonglong ()) {
v.set (tl::Variant (to_ulonglong (m_context, *v) & to_ulonglong (m_context, *b)));
v.set (tl::Variant (to_ulonglong (m_context, *v, 0) & to_ulonglong (m_context, *b, 1)));
} else if (v->is_longlong () || b->is_longlong ()) {
v.set (tl::Variant (to_longlong (m_context, *v) & to_longlong (m_context, *b)));
v.set (tl::Variant (to_longlong (m_context, *v, 0) & to_longlong (m_context, *b, 1)));
} else if (v->is_ulong () || b->is_ulong ()) {
v.set (tl::Variant (to_ulong (m_context, *v) & to_ulong (m_context, *b)));
v.set (tl::Variant (to_ulong (m_context, *v, 0) & to_ulong (m_context, *b, 1)));
} else {
v.set (tl::Variant (to_long (m_context, *v) & to_long (m_context, *b)));
v.set (tl::Variant (to_long (m_context, *v, 0) & to_long (m_context, *b, 1)));
}
}
};
@ -1625,13 +1625,13 @@ public:
v.swap (o);
} else if (v->is_ulonglong () || b->is_ulonglong ()) {
v.set (tl::Variant (to_ulonglong (m_context, *v) | to_ulonglong (m_context, *b)));
v.set (tl::Variant (to_ulonglong (m_context, *v, 0) | to_ulonglong (m_context, *b, 1)));
} else if (v->is_longlong () || b->is_longlong ()) {
v.set (tl::Variant (to_longlong (m_context, *v) | to_longlong (m_context, *b)));
v.set (tl::Variant (to_longlong (m_context, *v, 0) | to_longlong (m_context, *b, 1)));
} else if (v->is_ulong () || b->is_ulong ()) {
v.set (tl::Variant (to_ulong (m_context, *v) | to_ulong (m_context, *b)));
v.set (tl::Variant (to_ulong (m_context, *v, 0) | to_ulong (m_context, *b, 1)));
} else {
v.set (tl::Variant (to_long (m_context, *v) | to_long (m_context, *b)));
v.set (tl::Variant (to_long (m_context, *v, 0) | to_long (m_context, *b, 1)));
}
}
};
@ -1681,13 +1681,13 @@ public:
v.swap (o);
} else if (v->is_ulonglong () || b->is_ulonglong ()) {
v.set (tl::Variant (to_ulonglong (m_context, *v) ^ to_ulonglong (m_context, *b)));
v.set (tl::Variant (to_ulonglong (m_context, *v, 0) ^ to_ulonglong (m_context, *b, 1)));
} else if (v->is_longlong () || b->is_longlong ()) {
v.set (tl::Variant (to_longlong (m_context, *v) ^ to_longlong (m_context, *b)));
v.set (tl::Variant (to_longlong (m_context, *v, 0) ^ to_longlong (m_context, *b, 1)));
} else if (v->is_ulong () || b->is_ulong ()) {
v.set (tl::Variant (to_ulong (m_context, *v) ^ to_ulong (m_context, *b)));
v.set (tl::Variant (to_ulong (m_context, *v, 0) ^ to_ulong (m_context, *b, 1)));
} else {
v.set (tl::Variant (to_long (m_context, *v) ^ to_long (m_context, *b)));
v.set (tl::Variant (to_long (m_context, *v, 0) ^ to_long (m_context, *b, 1)));
}
}
};
@ -1826,7 +1826,7 @@ public:
} else if (v->is_ulonglong ()) {
v.set (-(long long)(v->to_ulonglong ()));
} else {
v.set (-to_double (m_context, *v));
v.set (-to_double (m_context, *v, 0));
}
}
};
@ -1881,7 +1881,7 @@ public:
} else if (v->is_ulonglong ()) {
v.set (~v->to_ulonglong ());
} else {
v.set (~to_long (m_context, *v));
v.set (~to_long (m_context, *v, 0));
}
}
};
@ -2388,7 +2388,7 @@ abs_f (const ExpressionParserContext &context, tl::Variant &out, const std::vect
} else if (v[0].is_double ()) {
out = fabs (v[0].to_double ());
} else {
out = labs (to_long (context, v[0]));
out = labs (to_long (context, v[0], 0));
}
}
@ -2463,7 +2463,7 @@ pow_f (const ExpressionParserContext &context, tl::Variant &out, const std::vect
throw EvalError (tl::to_string (tr ("'pow' function expects exactly two arguments")), context);
}
out = pow (to_double (context, vv [0]), to_double (context, vv [1]));
out = pow (to_double (context, vv [0], 0), to_double (context, vv [1], 1));
}
static void
@ -2473,7 +2473,7 @@ atan2_f (const ExpressionParserContext &context, tl::Variant &out, const std::ve
throw EvalError (tl::to_string (tr ("'atan2' function expects exactly two arguments")), context);
}
out = atan2 (to_double (context, vv [0]), to_double (context, vv [1]));
out = atan2 (to_double (context, vv [0], 0), to_double (context, vv [1], 1));
}
static void
@ -2690,10 +2690,10 @@ substr_f (const ExpressionParserContext &context, tl::Variant &out, const std::v
long len = -1;
if (vv.size () > 2) {
len = std::max (long (0), to_long (context, vv [2]));
len = std::max (long (0), to_long (context, vv [2], 2));
}
long l = to_long (context, vv [1]);
long l = to_long (context, vv [1], 1);
if (l < 0) {
l = long (s.size ()) + l;
if (l < 0) {
@ -2713,6 +2713,26 @@ substr_f (const ExpressionParserContext &context, tl::Variant &out, const std::v
}
}
static void
upcase_f (const ExpressionParserContext &context, tl::Variant &out, const std::vector <tl::Variant> &vv)
{
if (vv.size () != 1) {
throw EvalError (tl::to_string (tr ("'upcase' function expects one argument")), context);
}
out = tl::to_upper_case (vv [0].to_string ());
}
static void
downcase_f (const ExpressionParserContext &context, tl::Variant &out, const std::vector <tl::Variant> &vv)
{
if (vv.size () != 1) {
throw EvalError (tl::to_string (tr ("'upcase' function expects one argument")), context);
}
out = tl::to_lower_case (vv [0].to_string ());
}
static void
join_f (const ExpressionParserContext &context, tl::Variant &out, const std::vector <tl::Variant> &vv)
{
@ -2752,7 +2772,7 @@ item_f (const ExpressionParserContext &context, tl::Variant &out, const std::vec
throw EvalError (tl::to_string (tr ("First argument of 'item' function must be a list")), context);
}
long index = to_long (context, vv [1]);
long index = to_long (context, vv [1], 1);
if (index < 0 || index >= long (vv [0].end () - vv [0].begin ())) {
out = tl::Variant ();
} else {
@ -3042,6 +3062,8 @@ static EvalStaticFunction f55 ("file_exists", &file_exists_f);
static EvalStaticFunction f56 ("is_dir", &is_dir_f);
static EvalStaticFunction f57 ("combine", &combine_f);
static EvalStaticFunction f58 ("abs", &abs_f);
static EvalStaticFunction f59 ("upcase", &upcase_f);
static EvalStaticFunction f60 ("downcase", &downcase_f);
// ----------------------------------------------------------------------------
// Implementation of a constant wrapper

View File

@ -27,6 +27,7 @@
#include "tlDeferredExecution.h"
#include "tlObject.h"
#include "tlTimer.h"
#include "tlSleep.h"
#include <QNetworkAccessManager>
#include <QNetworkReply>
@ -447,9 +448,18 @@ InputHttpStreamPrivateData::read (char *b, size_t n)
issue_request (QUrl (tl::to_qstring (m_url)));
}
tl::Clock start_time = tl::Clock::current ();
while (mp_reply == 0 && (m_timeout <= 0.0 || (tl::Clock::current() - start_time).seconds () < m_timeout)) {
const unsigned long tick_ms = 10;
double time_waited = 0.0;
while (mp_reply == 0 && (m_timeout <= 0.0 || time_waited < m_timeout)) {
mp_stream->tick ();
// NOTE: as tick() includes waiting for the password dialog, we must not include
// the time spent there.
tl::msleep (tick_ms);
time_waited += tick_ms * 1e-3;
}
if (! mp_reply) {

View File

@ -784,6 +784,10 @@ TEST(6)
EXPECT_EQ (v.to_string (), std::string ("0"));
v = e.parse ("rfind('abcabc','x')").execute ();
EXPECT_EQ (v.to_string (), std::string ("nil"));
v = e.parse ("upcase('abcABC')").execute ();
EXPECT_EQ (v.to_string (), std::string ("ABCABC"));
v = e.parse ("downcase('abcABC')").execute ();
EXPECT_EQ (v.to_string (), std::string ("abcabc"));
v = e.parse ("len('abcabc')").execute ();
EXPECT_EQ (v.to_string (), std::string ("6"));
v = e.parse ("len([])").execute ();
@ -859,6 +863,14 @@ TEST(6)
msg = ex.msg();
}
EXPECT_EQ (msg, std::string ("My error"));
// argument index in error messages
msg.clear ();
try {
v = e.parse ("substr('abcabc',2,'xyz')").execute ();
} catch (tl::Exception &ex) {
msg = ex.msg();
}
EXPECT_EQ (msg, std::string ("Integer value expected for argument #3 at position 0 (substr('abcabc',2,'x..)"));
}
// compare ops

1100
testdata/algo/l2n_reader_7.l2n vendored Normal file

File diff suppressed because it is too large Load Diff

1126
testdata/algo/l2n_reader_au_7.l2n vendored Normal file

File diff suppressed because it is too large Load Diff

14
testdata/algo/nreader24.cir vendored Normal file
View File

@ -0,0 +1,14 @@
* Testing recursive call detection
.subckt c1 a b
x1 a b c2
.end
.subckt c2 a b
x1 a b c1
.ends
xtop vdd vss c2
.end

11
testdata/algo/nreader25.cir vendored Normal file
View File

@ -0,0 +1,11 @@
* Test: dismiss empty top level circuit
.subckt top a b
m1 a b a b nmos
.ends
* this triggered generation of a top level circuit
.param p1 17
.end

View File

@ -27,4 +27,6 @@ l1.drc(angle < 0.0).output(111, 0)
l1.drc(primary.angle > 0.0).output(112, 0)
l1.drc(primary.edges.angle == 90).output(113, 0)
l1.drc((angle == 0.0) + (angle == 90)).output(114, 0)
l1.drc(angle == 45).output(115, 0)
l1.drc(angle(absolute) == 45).output(116, 0)

View File

@ -23,6 +23,8 @@ l1.drc(corners(as_boxes) == -90).output(102, 0) # outer corners
l1.drc(corners(as_boxes) == 90).output(103, 0) # inner corners
l1.drc(corners(as_boxes) <= -90).output(104, 0)
l1.drc(corners(as_edge_pairs) == 90).output(105, 0)
l1.drc(corners(as_boxes, absolute) == 90).output(106, 0) # inner and outer corners
l1.drc(corners(as_boxes) != 90).output(107, 0) # not inner corners
l1.drc(middle).output(110, 0)
l1.drc(middle(as_dots)).output(111, 0)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

16
testdata/drc/drcSimpleTests_110.drc vendored Normal file
View File

@ -0,0 +1,16 @@
source($drc_test_source)
report("Report comment with\nanother line", $drc_test_report)
deep
l1 = input(1, 0)
l1 = l1.sized(0.1, 0.0)
l1.output("l1", "L1 (1/0 sized by x=100nm,y=0)")
l1.width(1.0.um).output("w1um", "w < 1µm\nFrom sized input")
l1s = l1.snapped(200.nm)
l1s.output("l1_snapped", "L1 snapped to 200nm")
l1s.width(1.0.um).output("w1um_snapped", "w < 1µm\nFrom snapped input")

BIN
testdata/drc/drcSimpleTests_110.gds vendored Normal file

Binary file not shown.

14
testdata/drc/drcSimpleTests_111.drc vendored Normal file
View File

@ -0,0 +1,14 @@
source($drc_test_source)
report("Report comment with\nanother line", $drc_test_report)
deep
l1 = input(1, 0)
l1s = l1.sized(0.1, 0.0)
l1.output([ "input", "L1" ], "Original layer")
l1s.output([ "input", "L1S" ], "Sized original layer")
l1s.width(1.0.um).output("w1um", "w < 1µm\nFrom sized input")

BIN
testdata/drc/drcSimpleTests_111.gds vendored Normal file

Binary file not shown.

16
testdata/drc/drcSimpleTests_112.drc vendored Normal file
View File

@ -0,0 +1,16 @@
source($drc_test_source)
report("Report comment with\nanother line", $drc_test_report)
deep
l1 = input(1, 0)
l1 = l1.sized(0.1, 0.0)
l1.output(["inputs", "l1"], "L1 (1/0 sized by x=100nm,y=0)")
l1.width(1.0.um).output("w1um", "w < 1µm\nAnother line")
l1s = l1.snapped(200.nm)
l1s.output([ "inputs", "l1_snapped" ], "L1 snapped to 200nm")
l1s.width(1.0.um).output("w1um_snapped", "w < 1µm\nFrom snapped input")

BIN
testdata/drc/drcSimpleTests_112.gds vendored Normal file

Binary file not shown.

View File

@ -14,10 +14,12 @@ end
a1 = input(1)
b1 = input(2)
c1 = input(3)
e1 = input(5)
a1.output(1, 0)
b1.output(2, 0)
c1.output(3, 0)
e1.output(5, 0)
c1.rounded_corners(0.5, 0.5, 16).output(1010, 0)
c1.smoothed(1.5).output(1011, 0)
@ -70,6 +72,11 @@ a1.corners(-90.0, as_edge_pairs).polygons(0).output(1063, 0)
a1.corners(-90.0, as_edge_pairs).first_edges.start_segments(0.1).extended(0.05, 0.05, 0.05, 0.05).output(1064, 0)
a1.corners(-90.0, as_edge_pairs).second_edges.start_segments(0.1).extended(0.05, 0.05, 0.05, 0.05).output(1065, 0)
e1.corners(90.0, as_boxes).sized(0.05).output(1066, 0)
e1.corners(90.0, absolute, as_boxes).sized(0.05).output(1067, 0)
e1.corners(90.0, negative, as_boxes).sized(0.05).output(1068, 0)
e1.corners(44.0 .. 46.0, absolute, as_boxes).sized(0.05).output(1069, 0)
a1.select { |p| p.bbox.width < 0.8 }.output(1100, 0)
a1.collect { |p| p.is_box? && p.bbox.enlarged(0.1, 0.1) }.output(1101, 0)
a1.collect_to_region { |p| p.is_box? && p.bbox.enlarged(0.1, 0.1) }.output(1102, 0)

View File

@ -18,9 +18,13 @@ ep.without_distance(0.25, nil).polygons(0).output(121, 0)
ep.with_angle(45.0).polygons(0).output(200, 0)
ep.with_angle(0.0).polygons(0).output(201, 0)
ep.with_angle(45.0..91.0).polygons(0).output(202, 0)
ep.with_angle(45.0, absolute).polygons(0).output(203, 0)
ep.with_angle(45.0..91.0, absolute).polygons(0).output(204, 0)
ep.with_angle(45.0, 91.0, absolute).polygons(0).output(205, 0)
ep.with_angle(45.0, both).polygons(0).output(210, 0)
ep.with_angle(0.0, both).polygons(0).output(211, 0)
ep.with_angle(45.0..91.0, both).polygons(0).output(212, 0)
ep.with_angle(45.0..91.0, absolute, both).polygons(0).output(213, 0)
ep.without_angle(45.0).polygons(0).output(220, 0)
ep.without_angle(0.0).polygons(0).output(221, 0)

35
testdata/drc/drcSimpleTests_93.drc vendored Normal file
View File

@ -0,0 +1,35 @@
source $drc_test_source
target $drc_test_target
if $drc_test_deep
deep
end
r = input(1, 0)
e = r.edges
r.output(1, 0)
r.with_angle(45.0).polygons(0).output(100, 0)
r.with_angle(90.0).polygons(0).output(101, 0)
r.with_angle(91.0..100.0).polygons(0).output(102, 0)
r.without_angle(45.0).polygons(0).output(120, 0)
r.without_angle(90.0).polygons(0).output(121, 0)
r.without_angle(45.0..100.0).polygons(0).output(122, 0)
e.with_angle(45.0).output(200, 0)
e.with_angle(0.0).output(201, 0)
e.with_angle(45.0..91.0).output(202, 0)
e.with_angle(45.0, absolute).output(203, 0)
e.with_angle(45.0..91.0, absolute).output(204, 0)
e.with_angle(45.0, 91.0, absolute).output(205, 0)
e.without_angle(45.0).output(220, 0)
e.without_angle(0.0).output(221, 0)
e.without_angle(45.0..91.0).output(222, 0)
e.without_angle(45.0, absolute).output(223, 0)
e.without_angle(45.0..91.0, absolute).output(224, 0)
e.without_angle(45.0, 91.0, absolute).output(225, 0)

BIN
testdata/drc/drcSimpleTests_93.gds vendored Normal file

Binary file not shown.

585
testdata/drc/drcSimpleTests_au110.lyrdb vendored Normal file
View File

@ -0,0 +1,585 @@
<?xml version="1.0" encoding="utf-8"?>
<report-database>
<description>Report comment with
another line</description>
<original-file/>
<generator>drc: script='.drc'</generator>
<top-cell>TOP</top-cell>
<tags>
</tags>
<categories>
<category>
<name>l1</name>
<description>L1 (1/0 sized by x=100nm,y=0)</description>
<categories>
</categories>
</category>
<category>
<name>w1um</name>
<description>w &lt; 1µm
From sized input</description>
<categories>
</categories>
</category>
<category>
<name>l1_snapped</name>
<description>L1 snapped to 200nm</description>
<categories>
</categories>
</category>
<category>
<name>w1um_snapped</name>
<description>w &lt; 1µm
From snapped input</description>
<categories>
</categories>
</category>
</categories>
<cells>
<cell>
<name>TOP</name>
<variant/>
<layout-name/>
<references>
</references>
</cell>
<cell>
<name>A</name>
<variant>r0</variant>
<layout-name>A</layout-name>
<references>
<ref>
<parent>TOP</parent>
<trans>r0 *1 0.5,1.2</trans>
</ref>
</references>
</cell>
<cell>
<name>A</name>
<variant>r90</variant>
<layout-name>A$VAR1</layout-name>
<references>
<ref>
<parent>TOP</parent>
<trans>r270 *1 2,2</trans>
</ref>
</references>
</cell>
<cell>
<name>A</name>
<variant>r0;r0(-0.1,0)</variant>
<layout-name>A</layout-name>
<references>
<ref>
<parent>TOP</parent>
<trans>r0 *1 0.5,1.2</trans>
</ref>
</references>
</cell>
<cell>
<name>A</name>
<variant>r0;r0</variant>
<layout-name>A$VAR1$1</layout-name>
<references>
<ref>
<parent>TOP</parent>
<trans>r0 *1 1.2,1.2</trans>
</ref>
</references>
</cell>
</cells>
<items>
<item>
<tags/>
<category>l1</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (3.7,3.9;3.3,4;3.6,4.6;5.1,4.9;5.3,4.9;5.2,4.2;5,4.2;3.804,4.285;3.9,3.9)</value>
</values>
</item>
<item>
<tags/>
<category>l1</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (2.1,3;2.1,3.6;2.9,3.6;2.9,3)</value>
</values>
</item>
<item>
<tags/>
<category>l1</category>
<cell>A:r0</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (-0.1,0;-0.1,1.6;0.3,1.6;0.3,0)</value>
</values>
</item>
<item>
<tags/>
<category>l1</category>
<cell>A:r90</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (0,-0.1;0,1.7;0.2,1.7;0.2,-0.1)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.7,3.9;3.3,4)|(3.3,4;3.6,4.6)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.7,3.9;3.3,4)|(3.6,4.6;4.256,4.731)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.7,3.9;3.3,4)|(3.804,4.285;3.9,3.9)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (4.53,4.233;3.804,4.285)|(3.455,4.31;3.6,4.6)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.3,4;3.6,4.6)|(3.804,4.285;3.9,3.9)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.9,3.9;3.7,3.9)|(3.3,4;3.6,4.6)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (4.266,4.733;5.1,4.9)|(5.3,4.9;5.2,4.2)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (5.2,4.2;5,4.2)|(4.138,4.708;5.1,4.9)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (5,4.2;3.804,4.285)|(3.6,4.6;5.1,4.9)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.9,3.9;3.7,3.9)|(3.6,4.6;4.408,4.762)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (5.1,4.9;5.3,4.9)|(5.3,4.9;5.2,4.2)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (5.2,4.2;5,4.2)|(5.1,4.9;5.3,4.9)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (5,4.2;4.343,4.247)|(5.1,4.9;5.3,4.9)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.9,3.9;3.7,3.9)|(3.804,4.285;3.9,3.9)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (2.1,3;2.1,3.6)|(2.9,3.6;2.9,3)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (2.9,3;2.1,3)|(2.1,3.6;2.9,3.6)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>A:r0</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (-0.1,0;-0.1,1.6)|(0.3,1.6;0.3,0)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>A:r90</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (0,-0.1;0,1.7)|(0.2,1.7;0.2,-0.1)</value>
</values>
</item>
<item>
<tags/>
<category>l1_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (3.4,4;3.6,4.6;5.2,5;5.4,5;5.2,4.2;3.8,4.2;4,4)</value>
</values>
</item>
<item>
<tags/>
<category>l1_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (2.2,3;2.2,3.6;3,3.6;3,3)</value>
</values>
</item>
<item>
<tags/>
<category>l1_snapped</category>
<cell>A:r0;r0(-0.1,0)</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (-0.1,0;-0.1,1.6;0.3,1.6;0.3,0)</value>
</values>
</item>
<item>
<tags/>
<category>l1_snapped</category>
<cell>A:r90</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (0,0;0,1.8;0.2,1.8;0.2,0)</value>
</values>
</item>
<item>
<tags/>
<category>l1_snapped</category>
<cell>A:r0;r0</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (0,0;0,1.6;0.4,1.6;0.4,0)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.467,4.2;3.6,4.6)|(4.521,4.2;3.8,4.2)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.4,4;3.55,4.45)|(3.8,4.2;4,4)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.4,4;3.6,4.6)|(4,4;3.4,4)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (4.314,4.778;5.2,5)|(5.4,5;5.2,4.2)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (5.2,4.2;3.8,4.2)|(3.6,4.6;5.2,5)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (4,4;3.4,4)|(3.6,4.6;4.547,4.837)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (5.2,5;5.4,5)|(5.4,5;5.2,4.2)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (5.2,4.2;4.6,4.2)|(5.2,5;5.4,5)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (4,4;3.4,4)|(3.8,4.2;4,4)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (2.2,3;2.2,3.6)|(3,3.6;3,3)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3,3;2.2,3)|(2.2,3.6;3,3.6)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>A:r0;r0(-0.1,0)</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (-0.1,0;-0.1,1.6)|(0.3,1.6;0.3,0)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>A:r90</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (0,0;0,1.8)|(0.2,1.8;0.2,0)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>A:r0;r0</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (0,0;0,1.6)|(0.4,1.6;0.4,0)</value>
</values>
</item>
</items>
</report-database>

382
testdata/drc/drcSimpleTests_au111.lyrdb vendored Normal file
View File

@ -0,0 +1,382 @@
<?xml version="1.0" encoding="utf-8"?>
<report-database>
<description>Report comment with
another line</description>
<original-file/>
<generator>drc: script='.drc'</generator>
<top-cell>TOP</top-cell>
<tags>
</tags>
<categories>
<category>
<name>input</name>
<description/>
<categories>
<category>
<name>L1</name>
<description>Original layer</description>
<categories>
</categories>
</category>
<category>
<name>L1S</name>
<description>Sized original layer</description>
<categories>
</categories>
</category>
</categories>
</category>
<category>
<name>w1um</name>
<description>w &lt; 1µm
From sized input</description>
<categories>
</categories>
</category>
</categories>
<cells>
<cell>
<name>TOP</name>
<variant/>
<layout-name/>
<references>
</references>
</cell>
<cell>
<name>A</name>
<variant>r0</variant>
<layout-name>A</layout-name>
<references>
<ref>
<parent>TOP</parent>
<trans>r0 *1 0.5,1.2</trans>
</ref>
</references>
</cell>
<cell>
<name>A</name>
<variant>r90</variant>
<layout-name>A$VAR1</layout-name>
<references>
<ref>
<parent>TOP</parent>
<trans>r270 *1 2,2</trans>
</ref>
</references>
</cell>
</cells>
<items>
<item>
<tags/>
<category>input.L1</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (3.8,3.9;3.4,4;3.7,4.6;5.2,4.9;5.1,4.2;3.7,4.3)</value>
</values>
</item>
<item>
<tags/>
<category>input.L1</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (2.2,3;2.2,3.6;2.8,3.6;2.8,3)</value>
</values>
</item>
<item>
<tags/>
<category>input.L1</category>
<cell>A:r0</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (0,0;0,1.6;0.2,1.6;0.2,0)</value>
</values>
</item>
<item>
<tags/>
<category>input.L1</category>
<cell>A:r90</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (0,0;0,1.6;0.2,1.6;0.2,0)</value>
</values>
</item>
<item>
<tags/>
<category>input.L1S</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (3.7,3.9;3.3,4;3.6,4.6;5.1,4.9;5.3,4.9;5.2,4.2;5,4.2;3.804,4.285;3.9,3.9)</value>
</values>
</item>
<item>
<tags/>
<category>input.L1S</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (2.1,3;2.1,3.6;2.9,3.6;2.9,3)</value>
</values>
</item>
<item>
<tags/>
<category>input.L1S</category>
<cell>A:r0</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (-0.1,0;-0.1,1.6;0.3,1.6;0.3,0)</value>
</values>
</item>
<item>
<tags/>
<category>input.L1S</category>
<cell>A:r90</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (0,-0.1;0,1.7;0.2,1.7;0.2,-0.1)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.7,3.9;3.3,4)|(3.3,4;3.6,4.6)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.7,3.9;3.3,4)|(3.6,4.6;4.256,4.731)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.7,3.9;3.3,4)|(3.804,4.285;3.9,3.9)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (4.53,4.233;3.804,4.285)|(3.455,4.31;3.6,4.6)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.3,4;3.6,4.6)|(3.804,4.285;3.9,3.9)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.9,3.9;3.7,3.9)|(3.3,4;3.6,4.6)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (4.266,4.733;5.1,4.9)|(5.3,4.9;5.2,4.2)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (5.2,4.2;5,4.2)|(4.138,4.708;5.1,4.9)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (5,4.2;3.804,4.285)|(3.6,4.6;5.1,4.9)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.9,3.9;3.7,3.9)|(3.6,4.6;4.408,4.762)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (5.1,4.9;5.3,4.9)|(5.3,4.9;5.2,4.2)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (5.2,4.2;5,4.2)|(5.1,4.9;5.3,4.9)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (5,4.2;4.343,4.247)|(5.1,4.9;5.3,4.9)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.9,3.9;3.7,3.9)|(3.804,4.285;3.9,3.9)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (2.1,3;2.1,3.6)|(2.9,3.6;2.9,3)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (2.9,3;2.1,3)|(2.1,3.6;2.9,3.6)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>A:r0</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (-0.1,0;-0.1,1.6)|(0.3,1.6;0.3,0)</value>
</values>
</item>
<item>
<tags/>
<category>w1um</category>
<cell>A:r90</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (0,-0.1;0,1.7)|(0.2,1.7;0.2,-0.1)</value>
</values>
</item>
</items>
</report-database>

596
testdata/drc/drcSimpleTests_au112.lyrdb vendored Normal file
View File

@ -0,0 +1,596 @@
<?xml version="1.0" encoding="utf-8"?>
<report-database>
<description>Report comment with
another line</description>
<original-file/>
<generator>drc: script='.drc'</generator>
<top-cell>TOP</top-cell>
<tags>
<tag>
<name>waived</name>
<description/>
</tag>
</tags>
<categories>
<category>
<name>inputs</name>
<description/>
<categories>
<category>
<name>l1</name>
<description>L1 (1/0 sized by x=100nm,y=0)</description>
<categories>
</categories>
</category>
<category>
<name>l1_snapped</name>
<description>L1 snapped to 200nm</description>
<categories>
</categories>
</category>
</categories>
</category>
<category>
<name>w1um</name>
<description>w &lt; 1µm
Another line</description>
<categories>
</categories>
</category>
<category>
<name>w1um_snapped</name>
<description>w &lt; 1µm
From snapped input</description>
<categories>
</categories>
</category>
</categories>
<cells>
<cell>
<name>TOP</name>
<variant/>
<layout-name/>
<references>
</references>
</cell>
<cell>
<name>A</name>
<variant>r0</variant>
<layout-name>A</layout-name>
<references>
<ref>
<parent>TOP</parent>
<trans>r0 *1 0.5,1.2</trans>
</ref>
</references>
</cell>
<cell>
<name>A</name>
<variant>r90</variant>
<layout-name>A$VAR1</layout-name>
<references>
<ref>
<parent>TOP</parent>
<trans>r270 *1 2,2</trans>
</ref>
</references>
</cell>
<cell>
<name>A</name>
<variant>r0;r0(-0.1,0)</variant>
<layout-name>A</layout-name>
<references>
<ref>
<parent>TOP</parent>
<trans>r0 *1 0.5,1.2</trans>
</ref>
</references>
</cell>
<cell>
<name>A</name>
<variant>r0;r0</variant>
<layout-name>A$VAR1$1</layout-name>
<references>
<ref>
<parent>TOP</parent>
<trans>r0 *1 1.2,1.2</trans>
</ref>
</references>
</cell>
</cells>
<items>
<item>
<tags/>
<category>inputs.l1</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (3.7,3.9;3.3,4;3.6,4.6;5.1,4.9;5.3,4.9;5.2,4.2;5,4.2;3.804,4.285;3.9,3.9)</value>
</values>
</item>
<item>
<tags/>
<category>inputs.l1</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (2.1,3;2.1,3.6;2.9,3.6;2.9,3)</value>
</values>
</item>
<item>
<tags/>
<category>inputs.l1</category>
<cell>A:r0</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (-0.1,0;-0.1,1.6;0.3,1.6;0.3,0)</value>
</values>
</item>
<item>
<tags/>
<category>inputs.l1</category>
<cell>A:r90</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (0,-0.1;0,1.7;0.2,1.7;0.2,-0.1)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.7,3.9;3.3,4)|(3.3,4;3.6,4.6)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.7,3.9;3.3,4)|(3.6,4.6;4.256,4.731)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.7,3.9;3.3,4)|(3.804,4.285;3.9,3.9)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (4.53,4.233;3.804,4.285)|(3.455,4.31;3.6,4.6)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.3,4;3.6,4.6)|(3.804,4.285;3.9,3.9)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.9,3.9;3.7,3.9)|(3.3,4;3.6,4.6)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (4.266,4.733;5.1,4.9)|(5.3,4.9;5.2,4.2)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (5.2,4.2;5,4.2)|(4.138,4.708;5.1,4.9)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (5,4.2;3.804,4.285)|(3.6,4.6;5.1,4.9)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.9,3.9;3.7,3.9)|(3.6,4.6;4.408,4.762)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment>First item von w1um</comment>
<image/>
<values>
<value>edge-pair: (5.1,4.9;5.3,4.9)|(5.3,4.9;5.2,4.2)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (5.2,4.2;5,4.2)|(5.1,4.9;5.3,4.9)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (5,4.2;4.343,4.247)|(5.1,4.9;5.3,4.9)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.9,3.9;3.7,3.9)|(3.804,4.285;3.9,3.9)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (2.1,3;2.1,3.6)|(2.9,3.6;2.9,3)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (2.9,3;2.1,3)|(2.1,3.6;2.9,3.6)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>A:r0</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (-0.1,0;-0.1,1.6)|(0.3,1.6;0.3,0)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>A:r90</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (0,-0.1;0,1.7)|(0.2,1.7;0.2,-0.1)</value>
</values>
</item>
<item>
<tags/>
<category>inputs.l1_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (3.4,4;3.6,4.6;5.2,5;5.4,5;5.2,4.2;3.8,4.2;4,4)</value>
</values>
</item>
<item>
<tags/>
<category>inputs.l1_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (2.2,3;2.2,3.6;3,3.6;3,3)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>inputs.l1_snapped</category>
<cell>A:r0;r0(-0.1,0)</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment>A comment on the only waived input shape</comment>
<image/>
<values>
<value>polygon: (-0.1,0;-0.1,1.6;0.3,1.6;0.3,0)</value>
</values>
</item>
<item>
<tags/>
<category>inputs.l1_snapped</category>
<cell>A:r90</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (0,0;0,1.8;0.2,1.8;0.2,0)</value>
</values>
</item>
<item>
<tags/>
<category>inputs.l1_snapped</category>
<cell>A:r0;r0</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (0,0;0,1.6;0.4,1.6;0.4,0)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.467,4.2;3.6,4.6)|(4.521,4.2;3.8,4.2)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.4,4;3.55,4.45)|(3.8,4.2;4,4)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.4,4;3.6,4.6)|(4,4;3.4,4)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (4.314,4.778;5.2,5)|(5.4,5;5.2,4.2)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (5.2,4.2;3.8,4.2)|(3.6,4.6;5.2,5)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (4,4;3.4,4)|(3.6,4.6;4.547,4.837)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (5.2,5;5.4,5)|(5.4,5;5.2,4.2)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (5.2,4.2;4.6,4.2)|(5.2,5;5.4,5)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (4,4;3.4,4)|(3.8,4.2;4,4)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (2.2,3;2.2,3.6)|(3,3.6;3,3)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3,3;2.2,3)|(2.2,3.6;3,3.6)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>A:r0;r0(-0.1,0)</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (-0.1,0;-0.1,1.6)|(0.3,1.6;0.3,0)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um_snapped</category>
<cell>A:r90</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (0,0;0,1.8)|(0.2,1.8;0.2,0)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>A:r0;r0</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment>A comment
With two lines</comment>
<image/>
<values>
<value>edge-pair: (0,0;0,1.6)|(0.4,1.6;0.4,0)</value>
</values>
</item>
</items>
</report-database>

View File

@ -0,0 +1,616 @@
<?xml version="1.0" encoding="utf-8"?>
<report-database>
<description>waiving
another line</description>
<original-file>/home/matthias/klayout/testdata/waiving.gds</original-file>
<generator>drc: script='/home/matthias/.klayout/drc/waiving.lydrc'</generator>
<top-cell>TOP</top-cell>
<tags>
<tag>
<name>waived</name>
<description/>
</tag>
<tag>
<name>red</name>
<description/>
</tag>
<tag>
<name>green</name>
<description/>
</tag>
<tag>
<name>blue</name>
<description/>
</tag>
<tag>
<name>yellow</name>
<description/>
</tag>
<tag>
<name>important</name>
<description/>
</tag>
</tags>
<categories>
<category>
<name>inputs</name>
<description/>
<categories>
<category>
<name>l1</name>
<description>L1 (1/0 sized by x=100nm,y=0)</description>
<categories>
</categories>
</category>
<category>
<name>l1_snapped</name>
<description>L1 snapped to 200nm</description>
<categories>
</categories>
</category>
</categories>
</category>
<category>
<name>w1um</name>
<description>w &lt; 1µm
Another line</description>
<categories>
</categories>
</category>
<category>
<name>w1um_snapped</name>
<description>w &lt; 1µm
From snapped input</description>
<categories>
</categories>
</category>
</categories>
<cells>
<cell>
<name>TOP</name>
<variant/>
<layout-name/>
<references>
</references>
</cell>
<cell>
<name>A</name>
<variant>r0</variant>
<layout-name>A</layout-name>
<references>
<ref>
<parent>TOP</parent>
<trans>r0 *1 0.5,1.2</trans>
</ref>
</references>
</cell>
<cell>
<name>A</name>
<variant>r90</variant>
<layout-name>A$VAR1</layout-name>
<references>
<ref>
<parent>TOP</parent>
<trans>r270 *1 2,2</trans>
</ref>
</references>
</cell>
<cell>
<name>A</name>
<variant>r0;r0(-0.1,0)</variant>
<layout-name>A</layout-name>
<references>
<ref>
<parent>TOP</parent>
<trans>r0 *1 0.5,1.2</trans>
</ref>
</references>
</cell>
<cell>
<name>A</name>
<variant>r0;r0</variant>
<layout-name>A$VAR1$1</layout-name>
<references>
<ref>
<parent>TOP</parent>
<trans>r0 *1 1.2,1.2</trans>
</ref>
</references>
</cell>
</cells>
<items>
<item>
<tags/>
<category>inputs.l1</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (3.7,3.9;3.3,4;3.6,4.6;5.1,4.9;5.3,4.9;5.2,4.2;5,4.2;3.804,4.285;3.9,3.9)</value>
</values>
</item>
<item>
<tags/>
<category>inputs.l1</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (2.1,3;2.1,3.6;2.9,3.6;2.9,3)</value>
</values>
</item>
<item>
<tags/>
<category>inputs.l1</category>
<cell>A:r0</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (-0.1,0;-0.1,1.6;0.3,1.6;0.3,0)</value>
</values>
</item>
<item>
<tags/>
<category>inputs.l1</category>
<cell>A:r90</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (0,-0.1;0,1.7;0.2,1.7;0.2,-0.1)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>TOP</cell>
<visited>true</visited>
<multiplicity>1</multiplicity>
<comment>First item von w1um</comment>
<image/>
<values>
<value>edge-pair: (5.1,4.9;5.3,4.9)|(5.3,4.9;5.2,4.2)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>TOP</cell>
<visited>true</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.9,3.9;3.7,3.9)|(3.6,4.6;4.408,4.762)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>TOP</cell>
<visited>true</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.9,3.9;3.7,3.9)|(3.3,4;3.6,4.6)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>TOP</cell>
<visited>true</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (5.2,4.2;5,4.2)|(4.138,4.708;5.1,4.9)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>TOP</cell>
<visited>true</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (2.9,3;2.1,3)|(2.1,3.6;2.9,3.6)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>TOP</cell>
<visited>true</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.9,3.9;3.7,3.9)|(3.804,4.285;3.9,3.9)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>TOP</cell>
<visited>true</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.7,3.9;3.3,4)|(3.3,4;3.6,4.6)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>TOP</cell>
<visited>true</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.3,4;3.6,4.6)|(3.804,4.285;3.9,3.9)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>TOP</cell>
<visited>true</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.7,3.9;3.3,4)|(3.6,4.6;4.256,4.731)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>TOP</cell>
<visited>true</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (4.53,4.233;3.804,4.285)|(3.455,4.31;3.6,4.6)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>TOP</cell>
<visited>true</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (2.1,3;2.1,3.6)|(2.9,3.6;2.9,3)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>TOP</cell>
<visited>true</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (5.2,4.2;5,4.2)|(5.1,4.9;5.3,4.9)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>TOP</cell>
<visited>true</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.7,3.9;3.3,4)|(3.804,4.285;3.9,3.9)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>TOP</cell>
<visited>true</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (5,4.2;4.343,4.247)|(5.1,4.9;5.3,4.9)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>TOP</cell>
<visited>true</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (4.266,4.733;5.1,4.9)|(5.3,4.9;5.2,4.2)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>TOP</cell>
<visited>true</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (5,4.2;3.804,4.285)|(3.6,4.6;5.1,4.9)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>A:r0</cell>
<visited>true</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (-0.1,0;-0.1,1.6)|(0.3,1.6;0.3,0)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um</category>
<cell>A:r90</cell>
<visited>true</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (0,-0.1;0,1.7)|(0.2,1.7;0.2,-0.1)</value>
</values>
</item>
<item>
<tags/>
<category>inputs.l1_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (3.4,4;3.6,4.6;5.2,5;5.4,5;5.2,4.2;3.8,4.2;4,4)</value>
</values>
</item>
<item>
<tags/>
<category>inputs.l1_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (2.2,3;2.2,3.6;3,3.6;3,3)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>inputs.l1_snapped</category>
<cell>A:r0;r0(-0.1,0)</cell>
<visited>true</visited>
<multiplicity>1</multiplicity>
<comment>A comment on the only waived input shape</comment>
<image/>
<values>
<value>polygon: (-0.1,0;-0.1,1.6;0.3,1.6;0.3,0)</value>
</values>
</item>
<item>
<tags/>
<category>inputs.l1_snapped</category>
<cell>A:r90</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (0,0;0,1.8;0.2,1.8;0.2,0)</value>
</values>
</item>
<item>
<tags/>
<category>inputs.l1_snapped</category>
<cell>A:r0;r0</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>polygon: (0,0;0,1.6;0.4,1.6;0.4,0)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (5.2,4.2;4.6,4.2)|(5.2,5;5.4,5)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (4,4;3.4,4)|(3.8,4.2;4,4)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (5.2,5;5.4,5)|(5.4,5;5.2,4.2)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (4,4;3.4,4)|(3.6,4.6;4.547,4.837)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (5.2,4.2;3.8,4.2)|(3.6,4.6;5.2,5)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (4.314,4.778;5.2,5)|(5.4,5;5.2,4.2)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.4,4;3.6,4.6)|(4,4;3.4,4)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.4,4;3.55,4.45)|(3.8,4.2;4,4)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>true</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3.467,4.2;3.6,4.6)|(4.521,4.2;3.8,4.2)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>true</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (3,3;2.2,3)|(2.2,3.6;3,3.6)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (2.2,3;2.2,3.6)|(3,3.6;3,3)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>A:r0;r0(-0.1,0)</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (-0.1,0;-0.1,1.6)|(0.3,1.6;0.3,0)</value>
</values>
</item>
<item>
<tags>waived</tags>
<category>w1um_snapped</category>
<cell>A:r90</cell>
<visited>true</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (0,0;0,1.8)|(0.2,1.8;0.2,0)</value>
</values>
</item>
<item>
<tags/>
<category>w1um_snapped</category>
<cell>A:r0;r0</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment>A comment
With two lines</comment>
<image/>
<values>
<value>edge-pair: (0,0;0,1.6)|(0.4,1.6;0.4,0)</value>
</values>
</item>
</items>
</report-database>

View File

@ -24,6 +24,7 @@
<cell>
<name>TOP</name>
<variant/>
<layout-name/>
<references>
</references>
</cell>
@ -35,6 +36,7 @@
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (-0.2,0.7;1,0.7)|(1,1.1;-0.2,1.1)</value>
@ -46,6 +48,7 @@
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (0,0;0,0.9)|(0.3,0.9;0.3,0)</value>
@ -57,6 +60,7 @@
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (0.3,0;0,0)|(0,0.9;0.3,0.9)</value>
@ -68,6 +72,7 @@
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (0.5,0;0.5,0.9)|(0.8,0.9;0.8,0)</value>
@ -79,6 +84,7 @@
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (0.8,0;0.5,0)|(0.5,0.9;0.8,0.9)</value>

View File

@ -18,6 +18,7 @@
<cell>
<name>TOP</name>
<variant/>
<layout-name/>
<references>
</references>
</cell>
@ -29,6 +30,7 @@
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (0,0.9;0.3,0.9)/(1,1.1;-0.2,1.1)</value>
@ -40,6 +42,7 @@
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<comment/>
<image/>
<values>
<value>edge-pair: (0.5,0.9;0.8,0.9)/(1,1.1;-0.2,1.1)</value>

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au93.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au93d.gds vendored Normal file

Binary file not shown.

Binary file not shown.

13
testdata/lvs/soft_connect1a.cir vendored Normal file
View File

@ -0,0 +1,13 @@
* Extracted by KLayout
.SUBCKT TOP A Q VDD SUBSTRATE|VSS
X$1 SUBSTRATE|VSS VDD VDD \$1 Q SUBSTRATE|VSS INV
X$2 SUBSTRATE|VSS VDD VDD A \$1 SUBSTRATE|VSS INV
.ENDS TOP
.SUBCKT INV \$1 \$2 \$3 \$4 \$5 SUBSTRATE
M$1 \$2 \$4 \$5 \$3 PMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U
+ PD=3.45U
M$2 \$1 \$4 \$5 SUBSTRATE NMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U
+ PD=3.45U
.ENDS INV

218
testdata/lvs/soft_connect1a.l2n vendored Normal file
View File

@ -0,0 +1,218 @@
#%l2n-klayout
W(TOP)
U(0.001)
L(l3 '1/0')
L(l4 '3/0')
L(l15 '3/1')
L(l8 '4/0')
L(l11 '5/0')
L(l12 '6/0')
L(l16 '6/1')
L(l13 '7/0')
L(l14 '8/0')
L(l17 '8/1')
L(l7)
L(l10)
L(l2)
L(l9)
L(l6)
C(l3 l3 l10)
C(l4 l4 l15 l11)
C(l15 l4 l15)
C(l8 l8 l12 l10 l2 l9 l6)
CS(l8 l10 l2 l9 l6)
C(l11 l4 l11 l12)
CS(l11 l4)
C(l12 l8 l11 l12 l16 l13)
C(l16 l12 l16)
C(l13 l12 l13 l14)
C(l14 l13 l14 l17)
C(l17 l14 l17)
C(l7 l7)
C(l10 l3 l8 l10)
CS(l10 l3)
C(l2 l8 l2)
C(l9 l8 l9)
C(l6 l8 l6)
G(l7 SUBSTRATE)
GS(l7 SUBSTRATE)
G(l9 SUBSTRATE)
GS(l9 SUBSTRATE)
H(W B('Net with incomplete wiring (soft-connected partial nets)') C(TOP) X('soft-connection-check'))
H(B('\tPartial net #1: TOP - VDD') C(TOP) Q('(0.6,3.95;0.6,4.85;4.5,4.85;4.5,3.95)'))
H(B('\tPartial net #2: TOP - $I4') C(TOP) Q('(5.1,3.95;5.1,4.85;9,4.85;9,3.95)'))
H(W B('Net with incomplete wiring (soft-connected partial nets)') C(TOP) X('soft-connection-check'))
H(B('\tPartial net #1: TOP - VSS') C(TOP) Q('(0.6,1.15;0.6,2.05;4.5,2.05;4.5,1.15)'))
H(B('\tPartial net #2: TOP - $I1') C(TOP) Q('(5.1,1.15;5.1,2.05;9,2.05;9,1.15)'))
K(PMOS MOS4)
K(NMOS MOS4)
D(D$PMOS PMOS
T(S
R(l2 (-900 -475) (775 950))
)
T(G
R(l4 (-125 -475) (250 950))
)
T(D
R(l2 (125 -475) (775 950))
)
T(B
R(l3 (-125 -475) (250 950))
)
)
D(D$NMOS NMOS
T(S
R(l6 (-900 -475) (775 950))
)
T(G
R(l4 (-125 -475) (250 950))
)
T(D
R(l6 (125 -475) (775 950))
)
T(B
R(l7 (-125 -475) (250 950))
)
)
X(INV
R((-1500 -800) (3000 4600))
N(1
R(l8 (290 -310) (220 220))
R(l8 (-220 180) (220 220))
R(l12 (-290 -690) (360 760))
R(l13 (-305 -705) (250 250))
R(l13 (-250 150) (250 250))
R(l14 (-2025 -775) (3000 900))
R(l6 (-1375 -925) (775 950))
)
N(2
R(l8 (290 2490) (220 220))
R(l8 (-220 180) (220 220))
R(l12 (-290 -690) (360 760))
R(l13 (-305 -705) (250 250))
R(l13 (-250 150) (250 250))
R(l14 (-2025 -775) (3000 900))
R(l2 (-1375 -925) (775 950))
)
N(3
R(l3 (-1500 1800) (3000 2000))
)
N(4
R(l4 (-125 -250) (250 2500))
R(l4 (-250 -3050) (250 1600))
R(l4 (-250 1200) (250 1600))
)
N(5
R(l8 (-510 -310) (220 220))
R(l8 (-220 180) (220 220))
R(l8 (-220 2180) (220 220))
R(l8 (-220 180) (220 220))
R(l12 (-290 -3530) (360 2840))
R(l12 (-360 -2800) (360 760))
R(l12 (-360 2040) (360 760))
R(l2 (-680 -855) (775 950))
R(l6 (-775 -3750) (775 950))
)
N(6 I(SUBSTRATE))
P(1)
P(2)
P(3)
P(4)
P(5)
P(6 I(SUBSTRATE))
D(1 D$PMOS
Y(0 2800)
E(L 0.25)
E(W 0.95)
E(AS 0.73625)
E(AD 0.73625)
E(PS 3.45)
E(PD 3.45)
T(S 5)
T(G 4)
T(D 2)
T(B 3)
)
D(2 D$NMOS
Y(0 0)
E(L 0.25)
E(W 0.95)
E(AS 0.73625)
E(AD 0.73625)
E(PS 3.45)
E(PD 3.45)
T(S 5)
T(G 4)
T(D 1)
T(B 6)
)
)
X(TOP
R((600 800) (8880 4600))
N(1
R(l4 (2920 2600) (2880 400))
R(l11 (-300 -300) (200 200))
R(l12 (-300 -300) (690 400))
)
N(2 I(A)
R(l4 (6600 2600) (2880 400))
R(l15 (-2380 -200) (0 0))
)
N(3 I(Q)
R(l12 (1810 2600) (690 400))
R(l16 (-400 -200) (0 0))
)
N(4 I(VDD)
R(l3 (4000 3400) (1600 2000))
R(l3 (-5000 -2000) (1000 2000))
R(l3 (6400 -2000) (1000 2000))
R(l8 (-8000 -900) (200 200))
R(l8 (-200 -600) (200 200))
R(l8 (7200 200) (200 200))
R(l8 (-200 -600) (200 200))
R(l12 (-7900 -350) (800 900))
R(l12 (6600 -900) (800 900))
R(l13 (-7900 -350) (200 200))
R(l13 (-200 -600) (200 200))
R(l13 (7200 200) (200 200))
R(l13 (-200 -600) (200 200))
R(l14 (-8000 -350) (1000 900))
R(l14 (6400 -900) (1000 900))
R(l17 (-4800 -450) (0 0))
)
N(5 I('SUBSTRATE,VSS')
R(l8 (1000 1700) (200 200))
R(l8 (-200 -600) (200 200))
R(l8 (7200 200) (200 200))
R(l8 (-200 -600) (200 200))
R(l12 (-7900 -350) (800 900))
R(l12 (6600 -900) (800 900))
R(l13 (-7900 -350) (200 200))
R(l13 (-200 -600) (200 200))
R(l13 (7200 200) (200 200))
R(l13 (-200 -600) (200 200))
R(l14 (-8000 -350) (1000 900))
R(l14 (6400 -900) (1000 900))
R(l17 (-4800 -550) (0 0))
)
P(2 I(A))
P(3 I(Q))
P(4 I(VDD))
P(5 I('SUBSTRATE,VSS'))
X(1 INV Y(3000 1600)
P(0 5)
P(1 4)
P(2 4)
P(3 1)
P(4 3)
P(5 5)
)
X(2 INV Y(6600 1600)
P(0 5)
P(1 4)
P(2 4)
P(3 2)
P(4 1)
P(5 5)
)
)

92
testdata/lvs/soft_connect1a.lvs vendored Normal file
View File

@ -0,0 +1,92 @@
$lvs_test_source && source($lvs_test_source)
if $lvs_test_target_l2n
report_netlist($lvs_test_target_l2n)
else
report_netlist
end
writer = write_spice(true, false)
$lvs_test_target_cir && target_netlist($lvs_test_target_cir, writer, "Extracted by KLayout")
deep
# Drawing layers
nwell = input(1, 0)
active = input(2, 0)
nplus = input(2, 1)
pplus = input(2, 2)
poly = input(3, 0)
poly_lbl = input(3, 1)
diff_cont = input(4, 0)
poly_cont = input(5, 0)
metal1 = input(6, 0)
metal1_lbl = input(6, 1)
via1 = input(7, 0)
metal2 = input(8, 0)
metal2_lbl = input(8, 1)
# Bulk layer for terminal provisioning
bulk = polygon_layer
psd = nil
nsd = nil
# Computed layers
active_in_nwell = active & nwell
pactive = active_in_nwell & pplus
ntie = active_in_nwell & nplus
pgate = pactive & poly
psd = pactive - pgate
active_outside_nwell = active - nwell
nactive = active_outside_nwell & nplus
ptie = active_outside_nwell & pplus
ngate = nactive & poly
nsd = nactive - ngate
# Device extraction
# PMOS transistor device extraction
extract_devices(mos4("PMOS"), { "SD" => psd, "G" => pgate, "W" => nwell,
"tS" => psd, "tD" => psd, "tG" => poly })
# NMOS transistor device extraction
extract_devices(mos4("NMOS"), { "SD" => nsd, "G" => ngate, "W" => bulk,
"tS" => nsd, "tD" => nsd, "tG" => poly })
# Define connectivity for netlist extraction
# Inter-layer
soft_connect(diff_cont, psd)
soft_connect(diff_cont, nsd)
soft_connect(diff_cont, ptie)
soft_connect(diff_cont, ntie)
soft_connect(ntie, nwell)
soft_connect(poly_cont, poly)
connect(diff_cont, metal1)
connect(poly_cont, metal1)
connect(metal1, via1)
connect(via1, metal2)
# attach labels
connect(poly, poly_lbl)
connect(metal1, metal1_lbl)
connect(metal2, metal2_lbl)
# Global
soft_connect_global(bulk, "SUBSTRATE")
soft_connect_global(ptie, "SUBSTRATE")
# Netlist section (NOTE: we only check log here)
netlist
netlist.simplify

View File

@ -294,6 +294,7 @@ class DBEdgePairs_TestClass < TestBase
ep4 = RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 10), RBA::Edge::new(10, 0, 10, 10))
r1 = RBA::EdgePairs::new([ ep1, ep2, ep3, ep4 ])
assert_equal(r1.with_angle(0, 90, false).to_s, "") # @@@
assert_equal(r1.with_distance(10, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_distance(5, 20, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
@ -310,15 +311,25 @@ class DBEdgePairs_TestClass < TestBase
assert_equal(r1.with_length_both(10, true).to_s, "(0,0;0,20)/(10,20;10,0)")
assert_equal(r1.with_angle(0, false).to_s, "")
assert_equal(r1.with_abs_angle(0, false).to_s, "")
assert_equal(r1.with_angle(0, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_abs_angle(0, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_angle(90, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_abs_angle(90, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_angle(0, 90, false).to_s, "")
assert_equal(r1.with_abs_angle(0, 90, false).to_s, "")
assert_equal(r1.with_angle(0, 90, false, true, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_abs_angle(0, 90, false, true, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_angle_both(0, false).to_s, "")
assert_equal(r1.with_abs_angle_both(0, false).to_s, "")
assert_equal(r1.with_angle_both(0, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_abs_angle_both(0, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_angle_both(90, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_abs_angle_both(90, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_angle_both(0, 90, false).to_s, "")
assert_equal(r1.with_abs_angle_both(0, 90, false).to_s, "")
assert_equal(r1.with_angle_both(0, 90, false, true, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_abs_angle_both(0, 90, false, true, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_area(0, false).to_s, "(0,0;0,10)/(10,0;10,10)")
assert_equal(r1.with_area(150, false).to_s, "(0,0;0,10)/(10,20;10,0)")

View File

@ -585,12 +585,19 @@ class DBEdges_TestClass < TestBase
r.insert(RBA::Edge::new(0, 0, 100, 0))
r.insert(RBA::Edge::new(100, 0, 100, 50))
assert_equal(r.with_angle(0, false).to_s, "(0,0;100,0)")
assert_equal(r.with_abs_angle(0, false).to_s, "(0,0;100,0)")
assert_equal(r.with_angle(0, true).to_s, "(100,0;100,50)")
assert_equal(r.with_abs_angle(0, true).to_s, "(100,0;100,50)")
assert_equal(r.with_angle(90, false).to_s, "(100,0;100,50)")
assert_equal(r.with_abs_angle(90, false).to_s, "(100,0;100,50)")
assert_equal(r.with_angle(90, true).to_s, "(0,0;100,0)")
assert_equal(r.with_abs_angle(90, true).to_s, "(0,0;100,0)")
assert_equal(r.with_angle(-10, 10, false).to_s, "(0,0;100,0)")
assert_equal(r.with_abs_angle(-10, 10, false).to_s, "(0,0;100,0)")
assert_equal(r.with_angle(-10, 10, true).to_s, "(100,0;100,50)")
assert_equal(r.with_abs_angle(-10, 10, true).to_s, "(100,0;100,50)")
assert_equal(r.with_angle(80, 100, false).to_s, "(100,0;100,50)")
assert_equal(r.with_abs_angle(80, 100, false).to_s, "(100,0;100,50)")
assert_equal(r.with_length(100, false).to_s, "(0,0;100,0)")
assert_equal(r.with_length(100, true).to_s, "(100,0;100,50)")
assert_equal(r.with_length(50, false).to_s, "(100,0;100,50)")

View File

@ -76,16 +76,22 @@ class RDB_TestClass < TestBase
assert_equal(cell.name, "cell_name")
assert_equal(cell.rdb_id, 1)
cell2 = db.create_cell("cell_name", "var1")
cell2 = db.create_cell("new_cell", "var1")
assert_equal(cell2.name, "new_cell")
assert_equal(cell2.layout_name, "")
assert_equal(cell2.qname, "new_cell:var1")
cell2 = db.create_cell("cell_name", "var1", "cell_name$1")
assert_equal(cell.name, "cell_name")
assert_equal(cell.qname, "cell_name:1")
assert_equal(db.cell_by_qname("cell_name:1").rdb_id, cell.rdb_id)
assert_equal(db.cell_by_id(cell.rdb_id).rdb_id, cell.rdb_id)
assert_equal(cell2.name, "cell_name")
assert_equal(cell2.layout_name, "cell_name$1")
assert_equal(cell2.qname, "cell_name:var1")
assert_equal(db.cell_by_qname("cell_name:var1").rdb_id, cell2.rdb_id)
assert_equal(db.cell_by_id(cell2.rdb_id).rdb_id, cell2.rdb_id)
assert_equal(cell2.rdb_id, 2)
assert_equal(cell2.rdb_id, 3)
assert_equal(cell.num_items, 0)
assert_equal(cell2.num_items, 0)
assert_equal(cell.num_items_visited, 0)
@ -98,19 +104,19 @@ class RDB_TestClass < TestBase
cc = []
db.each_cell { |c| cc.push(c) }
assert_equal(cc.size, 2)
assert_equal(cc.size, 3)
assert_equal(cc[0].rdb_id, cell.rdb_id)
assert_equal(cc[1].rdb_id, cell2.rdb_id)
assert_equal(cc[2].rdb_id, cell2.rdb_id)
cat = db.create_category("cat")
assert_equal(cat.database.inspect, db.inspect)
assert_equal(cat.name, "cat")
assert_equal(cat.rdb_id, 3)
assert_equal(cat.rdb_id, 4)
assert_equal(cat.path, "cat")
cats = db.create_category(cat, "subcat")
assert_equal(cats.name, "subcat")
assert_equal(cats.rdb_id, 4)
assert_equal(cats.rdb_id, 5)
assert_equal(cats.path, "cat.subcat")
assert_equal(cats.parent.rdb_id, cat.rdb_id)
@ -425,6 +431,10 @@ class RDB_TestClass < TestBase
assert_equal(item.image_str, "")
assert_equal(item.has_image?, false)
assert_equal(item.comment, "")
item.comment = "abc"
assert_equal(item.comment, "abc")
# can actually by any string, but only base64-encoded PNG images make sense
is="iVBORw0KGgoAAAANSUhEUgAAACoAAAA0CAIAAABzfT3nAAAAA3NCSVQICAjb4U/gAAAACXBIWXMAAA0SAAANOgHo3ZneAAAA3UlEQVRYhe2WwQ3DIAxFoco8XaGZIaeO43FyYgZYgYXcQ6SWuDGgBhWq/qccIvGCEd9SbAwAAPSGaW2lFR2rfWDpXrPpSe2SP10fvnn/PZHZH9IwbKFVZZ/Z6wMtZcjW02Bn2FVpZYdWdkr2nvh23S2FyDNJuVITpwmRjTGbNr0v20U5byNtJuuJt/fO2f93+UlbEJl5UjVPr3Y71EQ/PoPPlU+lDJtWlCt3GwCMG33BuJGAcWMEMG6c1jBudCyf/nzV8nbZPRohclFLHdGbZ8eNSjN1fmf0AACA1jwA4hKxu4C6P7EAAAAASUVORK5CYII="
item.image_str=is