mirror of https://github.com/KLayout/klayout.git
commit
e82d2140b3
6
build.sh
6
build.sh
|
|
@ -41,6 +41,7 @@ HAVE_CURL=0
|
|||
HAVE_EXPAT=0
|
||||
HAVE_GIT2=1
|
||||
HAVE_LSTREAM=1
|
||||
HAVE_CPP20=0
|
||||
|
||||
RUBYINCLUDE=""
|
||||
RUBYINCLUDE2=""
|
||||
|
|
@ -217,6 +218,9 @@ while [ "$*" != "" ]; do
|
|||
-nolstream)
|
||||
HAVE_LSTREAM=0
|
||||
;;
|
||||
-cpp20)
|
||||
HAVE_CPP20=1
|
||||
;;
|
||||
-qt5)
|
||||
echo "*** WARNING: -qt5 option is ignored - Qt version is auto-detected now."
|
||||
;;
|
||||
|
|
@ -275,6 +279,7 @@ while [ "$*" != "" ]; do
|
|||
echo " -libpng Use libpng instead of Qt for PNG generation"
|
||||
echo " -nolibgit2 Do not include libgit2 for Git package support"
|
||||
echo " -nolstream Do not include the LStream plugin"
|
||||
echo " -cpp20 Uses some C++20 features (e.g. atomics)"
|
||||
echo ""
|
||||
echo "Environment Variables:"
|
||||
echo ""
|
||||
|
|
@ -670,6 +675,7 @@ qmake_options=(
|
|||
HAVE_PNG="$HAVE_PNG"
|
||||
HAVE_GIT2="$HAVE_GIT2"
|
||||
HAVE_LSTREAM="$HAVE_LSTREAM"
|
||||
HAVE_CPP20="$HAVE_CPP20"
|
||||
PREFIX="$BIN"
|
||||
RPATH="$RPATH"
|
||||
KLAYOUT_VERSION="$KLAYOUT_VERSION"
|
||||
|
|
|
|||
|
|
@ -98,6 +98,20 @@ const std::string GenericWriterOptions::dxf_format_name = "DXF";
|
|||
const std::string GenericWriterOptions::cif_format_name = "CIF";
|
||||
const std::string GenericWriterOptions::mag_format_name = "MAG";
|
||||
|
||||
std::vector<std::string>
|
||||
GenericWriterOptions::all_format_names ()
|
||||
{
|
||||
std::vector<std::string> names;
|
||||
names.push_back (gds2_format_name);
|
||||
names.push_back (gds2text_format_name);
|
||||
names.push_back (oasis_format_name);
|
||||
names.push_back (lstream_format_name);
|
||||
names.push_back (dxf_format_name);
|
||||
names.push_back (cif_format_name);
|
||||
names.push_back (mag_format_name);
|
||||
return names;
|
||||
}
|
||||
|
||||
void
|
||||
GenericWriterOptions::add_options (tl::CommandLineOptions &cmd, const std::string &format)
|
||||
{
|
||||
|
|
@ -109,6 +123,15 @@ GenericWriterOptions::add_options (tl::CommandLineOptions &cmd, const std::strin
|
|||
"given factor."
|
||||
);
|
||||
|
||||
if (format.empty ()) {
|
||||
cmd << tl::arg (group +
|
||||
"-of|--format=format", &m_format, "Specifies the output format",
|
||||
"By default, the output format is derived from the file name suffix. "
|
||||
"You can also specify the format directly using this option. Allowed format names are: "
|
||||
+ tl::join (all_format_names (), ", ")
|
||||
);
|
||||
}
|
||||
|
||||
if (format.empty () || format == gds2_format_name || format == gds2text_format_name || format == oasis_format_name) {
|
||||
cmd << tl::arg (group +
|
||||
"-od|--dbu-out=dbu", &m_dbu, "Uses the specified database unit",
|
||||
|
|
@ -426,6 +449,22 @@ GenericWriterOptions::configure (db::SaveLayoutOptions &save_options, const db::
|
|||
save_options.set_keep_instances (m_keep_instances);
|
||||
save_options.set_write_context_info (m_write_context_info);
|
||||
|
||||
if (! m_format.empty ()) {
|
||||
|
||||
// check, if the format name is a valid one
|
||||
std::vector<std::string> af = all_format_names ();
|
||||
auto i = af.begin ();
|
||||
while (i != af.end () && *i != m_format) {
|
||||
++i;
|
||||
}
|
||||
if (i == af.end ()) {
|
||||
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Invalid fornat name %s. Allowed names are: %s")), m_format, tl::join (af, ", ")));
|
||||
}
|
||||
|
||||
save_options.set_format (m_format);
|
||||
|
||||
}
|
||||
|
||||
save_options.set_option_by_name ("gds2_max_vertex_count", m_gds2_max_vertex_count);
|
||||
save_options.set_option_by_name ("gds2_no_zero_length_paths", m_gds2_no_zero_length_paths);
|
||||
save_options.set_option_by_name ("gds2_multi_xy_records", m_gds2_multi_xy_records);
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#include "bdCommon.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace tl
|
||||
{
|
||||
|
|
@ -60,6 +61,11 @@ public:
|
|||
*/
|
||||
GenericWriterOptions (const db::SaveLayoutOptions &options);
|
||||
|
||||
/**
|
||||
* @brief Gets a list with all format names available
|
||||
*/
|
||||
static std::vector<std::string> all_format_names ();
|
||||
|
||||
/**
|
||||
* @brief Adds the generic options to the command line parser object
|
||||
* The format string gives a hint about the target format. Certain options will be
|
||||
|
|
@ -114,6 +120,7 @@ public:
|
|||
static const std::string mag_format_name;
|
||||
|
||||
private:
|
||||
std::string m_format;
|
||||
double m_scale_factor;
|
||||
double m_dbu;
|
||||
bool m_dont_write_empty_cells;
|
||||
|
|
|
|||
|
|
@ -945,7 +945,7 @@ std::string
|
|||
Cell::get_display_name () const
|
||||
{
|
||||
tl_assert (layout () != 0);
|
||||
if (is_ghost_cell () && empty ()) {
|
||||
if (is_real_ghost_cell ()) {
|
||||
return std::string ("(") + layout ()->cell_name (cell_index ()) + std::string (")");
|
||||
} else {
|
||||
return layout ()->cell_name (cell_index ());
|
||||
|
|
@ -959,6 +959,20 @@ Cell::set_name (const std::string &name)
|
|||
layout ()->rename_cell (cell_index (), name.c_str ());
|
||||
}
|
||||
|
||||
void
|
||||
Cell::set_ghost_cell (bool g)
|
||||
{
|
||||
// NOTE: this change is not undo managed
|
||||
if (m_ghost_cell != g) {
|
||||
|
||||
m_ghost_cell = g;
|
||||
tl_assert (layout () != 0);
|
||||
// To trigger a redraw and cell tree rebuild
|
||||
layout ()->cell_name_changed ();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Cell::check_locked () const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -925,15 +925,26 @@ public:
|
|||
return m_ghost_cell;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the cell is a "real" ghost cell
|
||||
*
|
||||
* A ghost cell is a real ghost cell only if the ghost cell flag is set
|
||||
* and the cell is empty. Only in that case for example the cell is written
|
||||
* to GDS files as a ghost cell.
|
||||
*
|
||||
* Otherwise, the ghost cell flag is mostly ignored.
|
||||
*/
|
||||
bool is_real_ghost_cell () const
|
||||
{
|
||||
return m_ghost_cell && empty ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the "ghost cell" flag
|
||||
*
|
||||
* See "is_ghost_cell" for a description of this property.
|
||||
*/
|
||||
void set_ghost_cell (bool g)
|
||||
{
|
||||
m_ghost_cell = g;
|
||||
}
|
||||
void set_ghost_cell (bool g);
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the cell is locked
|
||||
|
|
|
|||
|
|
@ -859,7 +859,7 @@ namespace
|
|||
struct generic_result_adaptor
|
||||
{
|
||||
public:
|
||||
generic_result_adaptor<T> (std::vector<std::unordered_set<T> > *results)
|
||||
generic_result_adaptor (std::vector<std::unordered_set<T> > *results)
|
||||
: mp_results (results)
|
||||
{
|
||||
m_intermediate.reserve (results->size ());
|
||||
|
|
|
|||
|
|
@ -1758,7 +1758,7 @@ public:
|
|||
* Creates a local operation which utilizes the operation tree. "node" is the root of the operation tree.
|
||||
* Ownership of the node is *not* transferred to the local operation.
|
||||
*/
|
||||
compound_local_operation<TS, TI, TR> (CompoundRegionOperationNode *node)
|
||||
compound_local_operation (CompoundRegionOperationNode *node)
|
||||
: mp_node (node)
|
||||
{ }
|
||||
|
||||
|
|
@ -1802,7 +1802,7 @@ public:
|
|||
* Creates a local operation which utilizes the operation tree. "node" is the root of the operation tree.
|
||||
* Ownership of the node is *not* transferred to the local operation.
|
||||
*/
|
||||
compound_local_operation_with_properties<TS, TI, TR> (CompoundRegionOperationNode *node, db::PropertyConstraint prop_constraint)
|
||||
compound_local_operation_with_properties (CompoundRegionOperationNode *node, db::PropertyConstraint prop_constraint)
|
||||
: mp_node (node), m_prop_constraint (prop_constraint)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
|
|
|
|||
|
|
@ -1686,6 +1686,69 @@ connected_clusters<T>::join_cluster_with (typename local_cluster<T>::id_type id,
|
|||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void
|
||||
connected_clusters<T>::join_clusters_with (typename local_cluster<T>::id_type id, typename std::set<typename local_cluster<T>::id_type>::const_iterator with_from, typename std::set<typename local_cluster<T>::id_type>::const_iterator with_to)
|
||||
{
|
||||
if (with_from != with_to && *with_from == id) {
|
||||
++with_from;
|
||||
}
|
||||
if (with_from == with_to) {
|
||||
return;
|
||||
}
|
||||
|
||||
connections_type &target = m_connections [id];
|
||||
std::set<connections_type::value_type> target_set;
|
||||
bool target_set_valid = false;
|
||||
|
||||
for (auto w = with_from; w != with_to; ++w) {
|
||||
|
||||
local_clusters<T>::join_cluster_with (id, *w);
|
||||
|
||||
// handle the connections by translating
|
||||
|
||||
typename std::map<typename local_cluster<T>::id_type, connections_type>::iterator tc = m_connections.find (*w);
|
||||
if (tc != m_connections.end ()) {
|
||||
|
||||
connections_type &to_join = tc->second;
|
||||
|
||||
for (connections_type::const_iterator c = to_join.begin (); c != to_join.end (); ++c) {
|
||||
m_rev_connections [*c] = id;
|
||||
}
|
||||
|
||||
if (target.empty ()) {
|
||||
|
||||
target.swap (to_join);
|
||||
|
||||
} else if (! to_join.empty ()) {
|
||||
|
||||
// Join while removing duplicates
|
||||
if (! target_set_valid) {
|
||||
target_set.insert (target.begin (), target.end ());
|
||||
target_set_valid = true;
|
||||
}
|
||||
|
||||
for (auto j = to_join.begin (); j != to_join.end (); ++j) {
|
||||
if (target_set.find (*j) == target_set.end ()) {
|
||||
target.push_back (*j);
|
||||
target_set.insert (*j);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
m_connections.erase (tc);
|
||||
|
||||
}
|
||||
|
||||
if (m_connected_clusters.find (*w) != m_connected_clusters.end ()) {
|
||||
m_connected_clusters.insert (id);
|
||||
m_connected_clusters.erase (*w);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
typename local_cluster<T>::id_type
|
||||
connected_clusters<T>::find_cluster_with_connection (const ClusterInstance &inst) const
|
||||
|
|
@ -1995,8 +2058,9 @@ public:
|
|||
|
||||
typename std::set<id_type>::const_iterator c = sc->begin ();
|
||||
typename std::set<id_type>::const_iterator cc = c;
|
||||
for (++cc; cc != sc->end (); ++cc) {
|
||||
mp_cell_clusters->join_cluster_with (*c, *cc);
|
||||
++cc;
|
||||
if (cc != sc->end ()) {
|
||||
mp_cell_clusters->join_clusters_with (*c, cc, sc->end ());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1274,6 +1274,13 @@ public:
|
|||
*/
|
||||
void join_cluster_with (typename local_cluster<T>::id_type id, typename local_cluster<T>::id_type with_id);
|
||||
|
||||
/**
|
||||
* @brief Joins a cluster id with the a set of clusters given by an iterator interval with_to .. with_from
|
||||
*
|
||||
* This function is equivalent to calling "join_cluster_with" multiple times, but more efficient.
|
||||
*/
|
||||
void join_clusters_with (typename local_cluster<T>::id_type id, typename std::set<typename local_cluster<T>::id_type>::const_iterator with_from, typename std::set<typename local_cluster<T>::id_type>::const_iterator with_to);
|
||||
|
||||
/**
|
||||
* @brief An iterator delivering all clusters (even the connectors)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -714,7 +714,6 @@ local_processor_result_computation_task<TS, TI, TR>::perform ()
|
|||
{
|
||||
mp_cell_contexts->compute_results (*mp_contexts, mp_cell, mp_op, m_output_layers, mp_proc);
|
||||
|
||||
// erase the contexts we don't need any longer
|
||||
{
|
||||
tl::MutexLocker locker (& mp_contexts->lock ());
|
||||
|
||||
|
|
@ -734,7 +733,10 @@ local_processor_result_computation_task<TS, TI, TR>::perform ()
|
|||
}
|
||||
#endif
|
||||
|
||||
mp_contexts->context_map ().erase (mp_cell);
|
||||
// release some memory
|
||||
auto ctx = mp_contexts->context_map ().find (mp_cell);
|
||||
tl_assert (ctx != mp_contexts->context_map ().end ());
|
||||
ctx->second.cleanup ();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -881,15 +883,6 @@ void local_processor<TS, TI, TR>::run (local_operation<TS, TI, TR> *op, unsigned
|
|||
compute_results (contexts, op, output_layers);
|
||||
}
|
||||
|
||||
template <class TS, class TI, class TR>
|
||||
void local_processor<TS, TI, TR>::push_results (db::Cell *cell, unsigned int output_layer, const std::unordered_set<TR> &result) const
|
||||
{
|
||||
if (! result.empty ()) {
|
||||
tl::MutexLocker locker (&cell->layout ()->lock ());
|
||||
cell->shapes (output_layer).insert (result.begin (), result.end ());
|
||||
}
|
||||
}
|
||||
|
||||
template <class TS, class TI, class TR>
|
||||
void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS, TI, TR> &contexts, const local_operation<TS, TI, TR> *op, unsigned int subject_layer, const std::vector<unsigned int> &intruder_layers) const
|
||||
{
|
||||
|
|
@ -1248,7 +1241,7 @@ local_processor<TS, TI, TR>::compute_results (local_processor_contexts<TS, TI, T
|
|||
typename local_processor_contexts<TS, TI, TR>::iterator cpc = contexts.context_map ().find (&mp_subject_layout->cell (*bu));
|
||||
if (cpc != contexts.context_map ().end ()) {
|
||||
cpc->second.compute_results (contexts, cpc->first, op, output_layers, this);
|
||||
contexts.context_map ().erase (cpc);
|
||||
cpc->second.cleanup (); // release some memory
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1261,6 +1254,24 @@ local_processor<TS, TI, TR>::compute_results (local_processor_contexts<TS, TI, T
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
// deliver the results
|
||||
{
|
||||
tl::MutexLocker locker (& mp_subject_layout->lock ());
|
||||
for (auto c = contexts.begin (); c != contexts.end (); ++c) {
|
||||
|
||||
db::Cell *cell = c->first;
|
||||
auto r = c->second.result ().begin ();
|
||||
auto rend = c->second.result ().end ();
|
||||
|
||||
for (auto o = output_layers.begin (); r != rend && o != output_layers.end (); ++o, ++r) {
|
||||
if (! r->empty ()) {
|
||||
cell->shapes (*o).insert (r->begin (), r->end ());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
|
|
|||
|
|
@ -244,9 +244,20 @@ public:
|
|||
return m_contexts.end ();
|
||||
}
|
||||
|
||||
const std::vector<std::unordered_set<TR> > &result () const
|
||||
{
|
||||
return m_result;
|
||||
}
|
||||
|
||||
void cleanup ()
|
||||
{
|
||||
m_contexts.clear ();
|
||||
}
|
||||
|
||||
private:
|
||||
const db::Cell *mp_intruder_cell;
|
||||
std::unordered_map<context_key_type, db::local_processor_cell_context<TS, TI, TR> > m_contexts;
|
||||
std::vector<std::unordered_set<TR> > m_result;
|
||||
};
|
||||
|
||||
template <class TS, class TI, class TR>
|
||||
|
|
@ -558,7 +569,6 @@ private:
|
|||
size_t get_progress () const;
|
||||
void compute_contexts (db::local_processor_contexts<TS, TI, TR> &contexts, db::local_processor_cell_context<TS, TI, TR> *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, const typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, db::Coord dist) const;
|
||||
void issue_compute_contexts (db::local_processor_contexts<TS, TI, TR> &contexts, db::local_processor_cell_context<TS, TI, TR> *parent_context, db::Cell *subject_parent, db::Cell *subject_cell, const db::ICplxTrans &subject_cell_inst, const db::Cell *intruder_cell, typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, db::Coord dist) const;
|
||||
void push_results (db::Cell *cell, unsigned int output_layer, const std::unordered_set<TR> &result) const;
|
||||
void compute_local_cell (const db::local_processor_contexts<TS, TI, TR> &contexts, db::Cell *subject_cell, const db::Cell *intruder_cell, const local_operation<TS, TI, TR> *op, const typename local_processor_cell_contexts<TS, TI, TR>::context_key_type &intruders, std::vector<std::unordered_set<TR> > &result) const;
|
||||
|
||||
bool subject_cell_is_breakout (db::cell_index_type ci) const
|
||||
|
|
|
|||
|
|
@ -446,7 +446,12 @@ local_processor_cell_contexts<TS, TI, TR>::compute_results (const local_processo
|
|||
proc->compute_local_cell (contexts, cell, mp_intruder_cell, op, *c->first, res);
|
||||
}
|
||||
|
||||
if (common.empty ()) {
|
||||
bool common_empty = true;
|
||||
for (auto c = common.begin (); common_empty && c != common.end (); ++c) {
|
||||
common_empty = c->empty ();
|
||||
}
|
||||
|
||||
if (common_empty) {
|
||||
|
||||
CRONOLOGY_COMPUTE_BRACKET(event_propagate)
|
||||
for (std::vector<unsigned int>::const_iterator o = output_layers.begin (); o != output_layers.end (); ++o) {
|
||||
|
|
@ -519,9 +524,30 @@ local_processor_cell_contexts<TS, TI, TR>::compute_results (const local_processo
|
|||
|
||||
}
|
||||
|
||||
for (std::vector<unsigned int>::const_iterator o = output_layers.begin (); o != output_layers.end (); ++o) {
|
||||
size_t oi = o - output_layers.begin ();
|
||||
proc->push_results (cell, *o, common[oi]);
|
||||
// store the results
|
||||
|
||||
bool common_empty = true;
|
||||
for (auto c = common.begin (); common_empty && c != common.end (); ++c) {
|
||||
common_empty = c->empty ();
|
||||
}
|
||||
|
||||
if (! common_empty) {
|
||||
if (m_result.empty ()) {
|
||||
m_result.swap (common);
|
||||
} else {
|
||||
if (m_result.size () < common.size ()) {
|
||||
m_result.resize (common.size ());
|
||||
}
|
||||
auto t = m_result.begin ();
|
||||
for (auto s = common.begin (); s != common.end (); ++s, ++t) {
|
||||
if (t->empty ()) {
|
||||
t->swap (*s);
|
||||
} else {
|
||||
t->insert (s->begin (), s->end ());
|
||||
s->clear ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1417,7 +1417,7 @@ Layout::add_cell (const char *name)
|
|||
if (cm != m_cell_map.end ()) {
|
||||
|
||||
const db::Cell &c= cell (cm->second);
|
||||
if (c.is_ghost_cell () && c.empty ()) {
|
||||
if (c.is_real_ghost_cell ()) {
|
||||
// ghost cells are available as new cells - the idea is to
|
||||
// treat them as non-existing.
|
||||
return cm->second;
|
||||
|
|
|
|||
|
|
@ -183,6 +183,14 @@ public:
|
|||
return m_busy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Issue a "cell name changed event"
|
||||
*/
|
||||
void cell_name_changed ()
|
||||
{
|
||||
cell_name_changed_event ();
|
||||
}
|
||||
|
||||
protected:
|
||||
friend class PropertiesRepository;
|
||||
|
||||
|
|
@ -191,14 +199,6 @@ protected:
|
|||
*/
|
||||
virtual void do_update () { }
|
||||
|
||||
/**
|
||||
* @brief Issue a "prop id's changed event"
|
||||
*/
|
||||
void cell_name_changed ()
|
||||
{
|
||||
cell_name_changed_event ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Issue a "layer properties changed event"
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -69,19 +69,19 @@ class polygon_properties_filter
|
|||
: public PolygonFilter, public PropertiesFilter
|
||||
{
|
||||
public:
|
||||
polygon_properties_filter<PolygonFilter> (const tl::Variant &name, const tl::GlobPattern &pattern, bool inverse)
|
||||
polygon_properties_filter (const tl::Variant &name, const tl::GlobPattern &pattern, bool inverse)
|
||||
: PropertiesFilter (name, pattern, inverse)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
polygon_properties_filter<PolygonFilter> (const tl::Variant &name, const tl::Variant &value, bool inverse)
|
||||
polygon_properties_filter(const tl::Variant &name, const tl::Variant &value, bool inverse)
|
||||
: PropertiesFilter (name, value, inverse)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
polygon_properties_filter<PolygonFilter> (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse)
|
||||
polygon_properties_filter (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse)
|
||||
: PropertiesFilter (name, from, to, inverse)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
|
|
@ -103,19 +103,19 @@ class generic_properties_filter
|
|||
: public BasicFilter, public PropertiesFilter
|
||||
{
|
||||
public:
|
||||
generic_properties_filter<BasicFilter, ShapeType> (const tl::Variant &name, const tl::GlobPattern &pattern, bool inverse)
|
||||
generic_properties_filter (const tl::Variant &name, const tl::GlobPattern &pattern, bool inverse)
|
||||
: PropertiesFilter (name, pattern, inverse)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
generic_properties_filter<BasicFilter, ShapeType> (const tl::Variant &name, const tl::Variant &value, bool inverse)
|
||||
generic_properties_filter (const tl::Variant &name, const tl::Variant &value, bool inverse)
|
||||
: PropertiesFilter (name, value, inverse)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
generic_properties_filter<BasicFilter, ShapeType> (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse)
|
||||
generic_properties_filter (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse)
|
||||
: PropertiesFilter (name, from, to, inverse)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include "tlString.h"
|
||||
#include "tlAssert.h"
|
||||
#include "tlHash.h"
|
||||
#include "tlStaticObjects.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
|
@ -348,13 +349,21 @@ PropertiesSet::hash () const
|
|||
// ----------------------------------------------------------------------------------
|
||||
// PropertiesRepository implementation
|
||||
|
||||
static PropertiesRepository s_instance;
|
||||
static PropertiesRepository *sp_global_instance = 0;
|
||||
static PropertiesRepository *sp_temp_instance = 0;
|
||||
|
||||
PropertiesRepository &
|
||||
PropertiesRepository::instance ()
|
||||
{
|
||||
return sp_temp_instance ? *sp_temp_instance : s_instance;
|
||||
if (sp_temp_instance) {
|
||||
return *sp_temp_instance;
|
||||
} else {
|
||||
if (! sp_global_instance) {
|
||||
sp_global_instance = new PropertiesRepository ();
|
||||
tl::StaticObjects::reg (&sp_global_instance);
|
||||
}
|
||||
return *sp_global_instance;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ struct simple_result_inserter
|
|||
{
|
||||
typedef TR value_type;
|
||||
|
||||
simple_result_inserter<TR> (std::unordered_set<TR> &result)
|
||||
simple_result_inserter (std::unordered_set<TR> &result)
|
||||
: mp_result (&result)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
|
|
@ -232,6 +232,8 @@ check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cel
|
|||
bool take_all = edge_check.has_negative_edge_output () || intruders.empty ();
|
||||
|
||||
db::Box common_box;
|
||||
bool subjects_are_fully_covered = false;
|
||||
|
||||
if (! take_all) {
|
||||
|
||||
db::Vector e (edge_check.distance (), edge_check.distance ());
|
||||
|
|
@ -241,14 +243,24 @@ check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cel
|
|||
subject_box += db::box_convert<TS> () (**i);
|
||||
}
|
||||
|
||||
if (edge_check.requires_different_layers ()) {
|
||||
db::Box intruder_box;
|
||||
common_box = subject_box.enlarged (e);
|
||||
|
||||
if (edge_check.requires_different_layers () && ! common_box.empty ()) {
|
||||
|
||||
subjects_are_fully_covered = false;
|
||||
|
||||
db::Box all_intruders_box;
|
||||
|
||||
for (auto i = intruders.begin (); i != intruders.end (); ++i) {
|
||||
intruder_box += db::box_convert<TI> () (**i);
|
||||
db::Box intruder_box = db::box_convert<TI> () (**i);
|
||||
if (! subjects_are_fully_covered && (*i)->is_box () && common_box.inside (intruder_box)) {
|
||||
subjects_are_fully_covered = true;
|
||||
}
|
||||
all_intruders_box += intruder_box.enlarged (e);
|
||||
}
|
||||
common_box = subject_box.enlarged (e) & intruder_box.enlarged (e);
|
||||
} else {
|
||||
common_box = subject_box.enlarged (e);
|
||||
|
||||
common_box &= all_intruders_box;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -306,6 +318,22 @@ check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cel
|
|||
|
||||
// empty intruders
|
||||
|
||||
} else if (subjects_are_fully_covered) {
|
||||
|
||||
// optimization: can use a single box for the intruders
|
||||
|
||||
n = 1;
|
||||
|
||||
db::Point ul = common_box.upper_left ();
|
||||
db::Point lr = common_box.lower_right ();
|
||||
|
||||
poly_check.enter (db::Edge (common_box.p1 (), ul), n);
|
||||
poly_check.enter (db::Edge (ul, common_box.p2 ()), n);
|
||||
poly_check.enter (db::Edge (common_box.p2 (), lr), n);
|
||||
poly_check.enter (db::Edge (lr, common_box.p1 ()), n);
|
||||
|
||||
n += 2;
|
||||
|
||||
} else if (! m_other_is_merged && (intruders.size () > 1 || ! (*intruders.begin ())->is_box ())) {
|
||||
|
||||
// NOTE: this local merge is not necessarily giving the same results than a global merge before running
|
||||
|
|
|
|||
|
|
@ -3450,6 +3450,9 @@ Class<db::Cell> decl_Cell ("db", "Cell",
|
|||
"To satisfy the references inside the layout, a dummy cell is created in this case\n"
|
||||
"which has the \"ghost cell\" flag set to true.\n"
|
||||
"\n"
|
||||
"A ghost cell is a real ghost cell only if the cell is empty. In that case, it is written "
|
||||
"as a ghost cell to GDS files for example. If a cell is not empty, this flag is ignored.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.20.\n"
|
||||
) +
|
||||
gsi::method ("ghost_cell=", &db::Cell::set_ghost_cell, gsi::arg ("flag"),
|
||||
|
|
|
|||
|
|
@ -503,3 +503,19 @@ TEST(SameValueDifferentTypes)
|
|||
EXPECT_EQ (db::property_name (rp.prop_name_id ((int) 5)).to_parsable_string (), "#5");
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ComplexTypes)
|
||||
{
|
||||
// This is also a smoke test: we intentionally register globally as the finalization code
|
||||
// is critical: without the right destruction order we destroy the class object before the
|
||||
// variant and trigger an assertion (pure virtual function called)
|
||||
|
||||
db::PropertiesSet ps;
|
||||
ps.insert (tl::Variant (17), db::DBox (0, 0, 1.5, 2.5));
|
||||
|
||||
db::properties_id_type pid = db::properties_id (ps);
|
||||
|
||||
auto const &ps_out = db::properties (pid);
|
||||
|
||||
EXPECT_EQ (ps_out.to_dict_var ().to_string (), "{17=>(0,0;1.5,2.5)}");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -942,15 +942,18 @@ module DRC
|
|||
# Turns profiling on or off (default). In profiling mode, the
|
||||
# system will collect statistics about rules executed, their execution time
|
||||
# and memory information. The argument specifies how many operations to
|
||||
# print at the end of the run. Without an argument, all operations are
|
||||
# print at the end of the run. Without an argument or when passing "true", all operations are
|
||||
# printed. Passing "false" for the argument will disable profiling. This is the
|
||||
# default.
|
||||
|
||||
def profile(n = 0)
|
||||
if !n.is_a?(1.class) && n != nil && n != false
|
||||
raise("Argument to 'profile' must be either an integer number or nil")
|
||||
if !n.is_a?(1.class) && n != nil && n != false && n != true
|
||||
raise("Argument to 'profile' must be either an integer number, true, false or nil")
|
||||
end
|
||||
@profile = !!n
|
||||
if n == true
|
||||
n = 0
|
||||
end
|
||||
@profile_n = [n || 0, 0].max
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
<string>Round Corners</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="2" column="0" colspan="3">
|
||||
|
|
|
|||
|
|
@ -1770,6 +1770,7 @@ PartialService::edit_cancel ()
|
|||
|
||||
ui ()->ungrab_mouse (this);
|
||||
|
||||
clear_mouse_cursors ();
|
||||
close_editor_hooks (false);
|
||||
|
||||
selection_to_view ();
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ template <class X, class Y> struct address_of;
|
|||
template<class X>
|
||||
struct address_of<X, X>
|
||||
{
|
||||
address_of<X, X> () : b () { }
|
||||
address_of () : b () { }
|
||||
const void *operator() (const X &x) const { b = x; return &b; }
|
||||
mutable X b;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -204,12 +204,16 @@ msvc {
|
|||
# -Wno-reserved-user-defined-literal \
|
||||
#
|
||||
|
||||
lessThan(QT_MAJOR_VERSION, 6) {
|
||||
# because we use unordered_map/unordered_set:
|
||||
QMAKE_CXXFLAGS += -std=c++11
|
||||
equals(HAVE_CPP20, "1") {
|
||||
QMAKE_CXXFLAGS += -std=c++20
|
||||
DEFINES += HAVE_CPP20
|
||||
} else {
|
||||
# because we use unordered_map/unordered_set:
|
||||
QMAKE_CXXFLAGS += -std=c++17
|
||||
lessThan(QT_MAJOR_VERSION, 6) {
|
||||
# because we use unordered_map/unordered_set:
|
||||
QMAKE_CXXFLAGS += -std=c++11
|
||||
} else {
|
||||
QMAKE_CXXFLAGS += -std=c++17
|
||||
}
|
||||
}
|
||||
|
||||
win32 {
|
||||
|
|
|
|||
|
|
@ -86,87 +86,6 @@
|
|||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QFrame" name="frame_5">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="_14">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QComboBox" name="fill_area_cbx">
|
||||
<property name="toolTip">
|
||||
<string>Select how the region to fill is specified.</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>All (whole cell)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Shapes on layer ...</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Selected shapes</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Single box with ...</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Ruler bounding boxes</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="3">
|
||||
<widget class="QFrame" name="frame_6">
|
||||
<property name="frameShape">
|
||||
|
|
@ -277,22 +196,6 @@
|
|||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>241</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
|
|
@ -316,6 +219,22 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>241</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_6">
|
||||
|
|
@ -436,6 +355,87 @@
|
|||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QFrame" name="frame_5">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="_14">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QComboBox" name="fill_area_cbx">
|
||||
<property name="toolTip">
|
||||
<string>Select how the region to fill is specified.</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>All (whole cell)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Shapes on layer ...</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Selected shapes</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Single box with ...</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Ruler bounding boxes</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
@ -491,6 +491,9 @@
|
|||
<property name="toolTip">
|
||||
<string>The fill will not be generated over the areas specified by these layers</string>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>All layers</string>
|
||||
|
|
@ -810,6 +813,83 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame_3">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Raster origin</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="origin_le">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>(x, y of lower-left corner of fill cell in µm)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>135</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="second_order_fill_cb">
|
||||
<property name="title">
|
||||
|
|
@ -837,13 +917,6 @@
|
|||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="1" column="2">
|
||||
<widget class="QLabel" name="label_60">
|
||||
<property name="text">
|
||||
<string>µm (keep distance between fill cells unless stitched)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="3">
|
||||
<widget class="QLineEdit" name="fill_cell_2nd_le">
|
||||
<property name="toolTip">
|
||||
|
|
@ -851,16 +924,17 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="fill2_margin_le">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
<item row="1" column="2">
|
||||
<widget class="QLabel" name="label_60">
|
||||
<property name="text">
|
||||
<string>µm (keep distance between fill cells unless stitched)</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Leave empty for no distance. Otherwise enter a distance in micron (can be anisotropic in the form "dx,dy")</string>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_57">
|
||||
<property name="text">
|
||||
<string>Fill cell margin </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
@ -871,26 +945,6 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>141</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_57">
|
||||
<property name="text">
|
||||
<string>Fill cell margin </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_59">
|
||||
<property name="sizePolicy">
|
||||
|
|
@ -904,16 +958,6 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="5">
|
||||
<widget class="QLabel" name="label_58">
|
||||
<property name="text">
|
||||
<string>The second order fill cell is used to fill space remaining from the first fill step. Thus, the second order fill cell must be smaller than the first order fill cell. The boundary layer must be the same for the second order fill cell.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_56">
|
||||
<property name="text">
|
||||
|
|
@ -980,6 +1024,42 @@
|
|||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>141</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="5">
|
||||
<widget class="QLabel" name="label_58">
|
||||
<property name="text">
|
||||
<string>The second order fill cell is used to fill space remaining from the first fill step. Thus, the second order fill cell must be smaller than the first order fill cell. The boundary layer must be the same for the second order fill cell.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="fill2_margin_le">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Leave empty for no distance. Otherwise enter a distance in micron (can be anisotropic in the form "dx,dy")</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
@ -1030,6 +1110,7 @@
|
|||
<tabstop>row_le</tabstop>
|
||||
<tabstop>column_le</tabstop>
|
||||
<tabstop>enhanced_cb</tabstop>
|
||||
<tabstop>origin_le</tabstop>
|
||||
<tabstop>second_order_fill_cb</tabstop>
|
||||
<tabstop>fill_cell_2nd_le</tabstop>
|
||||
<tabstop>choose_fc_2nd_pb</tabstop>
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ FillDialog::FillDialog (QWidget *parent, LayoutViewBase *view)
|
|||
|
||||
fill_area_stack->setCurrentIndex (0);
|
||||
connect (fill_area_cbx, SIGNAL (currentIndexChanged (int)), this, SLOT (fill_area_changed (int)));
|
||||
connect (enhanced_cb, SIGNAL (stateChanged (int)), this, SLOT (enhanced_fill_changed (int)));
|
||||
connect (button_box, SIGNAL (accepted ()), this, SLOT (ok_pressed ()));
|
||||
connect (choose_fc_pb, SIGNAL (clicked ()), this, SLOT (choose_fc ()));
|
||||
connect (choose_fc_2nd_pb, SIGNAL (clicked ()), this, SLOT (choose_fc_2nd ()));
|
||||
|
|
@ -167,6 +168,8 @@ FillDialog::generate_fill (const FillParameters &fp)
|
|||
|
||||
bool enhanced_fill = enhanced_cb->isChecked ();
|
||||
|
||||
db::Point fill_origin = db::CplxTrans (ly.dbu ()).inverted () * fp.fill_origin;
|
||||
|
||||
db::Coord exclude_x = db::coord_traits<db::Coord>::rounded (fp.exclude_distance.x () / ly.dbu ());
|
||||
db::Coord exclude_y = db::coord_traits<db::Coord>::rounded (fp.exclude_distance.y () / ly.dbu ());
|
||||
|
||||
|
|
@ -232,8 +235,6 @@ FillDialog::generate_fill (const FillParameters &fp)
|
|||
fill_region.merge ();
|
||||
}
|
||||
|
||||
db::Box fr_bbox = fill_region.bbox ();
|
||||
|
||||
if (tl::verbosity () >= 20) {
|
||||
tl::info << "Collecting exclude areas";
|
||||
}
|
||||
|
|
@ -263,8 +264,6 @@ FillDialog::generate_fill (const FillParameters &fp)
|
|||
// Perform the NOT operation to create the fill region
|
||||
fill_region -= es;
|
||||
|
||||
db::Region new_fill_area;
|
||||
|
||||
int step = 0;
|
||||
|
||||
do {
|
||||
|
|
@ -276,7 +275,7 @@ FillDialog::generate_fill (const FillParameters &fp)
|
|||
}
|
||||
|
||||
if (! enhanced_fill) {
|
||||
db::fill_region (cv.cell (), fill_region, fill_cell->cell_index (), fc_bbox, row_step, column_step, fr_bbox.p1 (), false, fill_cell2 ? &fill_region : 0, fill_margin, fill_cell2 ? &fill_region : 0);
|
||||
db::fill_region (cv.cell (), fill_region, fill_cell->cell_index (), fc_bbox, row_step, column_step, fill_origin, false, fill_cell2 ? &fill_region : 0, fill_margin, fill_cell2 ? &fill_region : 0);
|
||||
} else {
|
||||
db::fill_region_repeat (cv.cell (), fill_region, fill_cell->cell_index (), fc_bbox, row_step, column_step, fill_margin, fill_cell2 ? &fill_region : 0);
|
||||
}
|
||||
|
|
@ -478,6 +477,20 @@ FillDialog::get_fill_parameters ()
|
|||
|
||||
fp.enhanced_fill = enhanced_cb->isChecked ();
|
||||
|
||||
// read origin
|
||||
x = 0.0, y = 0.0;
|
||||
s = tl::to_string (origin_le->text ());
|
||||
ex = tl::Extractor (s.c_str ());
|
||||
if (ex.try_read (x)) {
|
||||
if (ex.test (",") && ex.try_read (y)) {
|
||||
// take x, y
|
||||
} else {
|
||||
y = x;
|
||||
}
|
||||
}
|
||||
|
||||
fp.fill_origin = db::DPoint (x, y);
|
||||
|
||||
db::DBox fc_bbox = db::CplxTrans (cv->layout ().dbu ()) * (fc_bbox_layer < 0 ? fill_cell->bbox () : fill_cell->bbox (fc_bbox_layer));
|
||||
if (fc_bbox.empty ()) {
|
||||
if (fc_bbox_layer >= 0) {
|
||||
|
|
@ -492,7 +505,7 @@ FillDialog::get_fill_parameters ()
|
|||
if (ex.try_read (x) && ex.test (",") && ex.try_read (y)) {
|
||||
fp.row_step = db::DVector (x, y);
|
||||
} else {
|
||||
fp.row_step = db::DVector (fc_bbox.width (), 0.0);
|
||||
fp.row_step = db::DVector (fc_bbox.width () + fp.fill_cell_margin.x (), 0.0);
|
||||
}
|
||||
|
||||
s = tl::to_string (column_le->text ());
|
||||
|
|
@ -500,7 +513,7 @@ FillDialog::get_fill_parameters ()
|
|||
if (ex.try_read (x) && ex.test (",") && ex.try_read (y)) {
|
||||
fp.column_step = db::DVector (x, y);
|
||||
} else {
|
||||
fp.column_step = db::DVector (0.0, fc_bbox.height ());
|
||||
fp.column_step = db::DVector (0.0, fc_bbox.height () + fp.fill_cell_margin.y ());
|
||||
}
|
||||
|
||||
fp.fc_bbox = fc_bbox;
|
||||
|
|
@ -528,7 +541,7 @@ FillDialog::get_fill_parameters ()
|
|||
if (ex.try_read (x) && ex.test (",") && ex.try_read (y)) {
|
||||
fp.row_step2 = db::DVector (x, y);
|
||||
} else {
|
||||
fp.row_step2 = db::DVector (fc_bbox2.width (), 0.0);
|
||||
fp.row_step2 = db::DVector (fc_bbox2.width () + fp.fill_cell_margin2.x (), 0.0);
|
||||
}
|
||||
|
||||
s = tl::to_string (column_2nd_le->text ());
|
||||
|
|
@ -536,7 +549,7 @@ FillDialog::get_fill_parameters ()
|
|||
if (ex.try_read (x) && ex.test (",") && ex.try_read (y)) {
|
||||
fp.column_step2 = db::DVector (x, y);
|
||||
} else {
|
||||
fp.column_step2 = db::DVector (0.0, fc_bbox2.height ());
|
||||
fp.column_step2 = db::DVector (0.0, fc_bbox2.height () + fp.fill_cell_margin2.y ());
|
||||
}
|
||||
|
||||
fp.fc_bbox2 = fc_bbox2;
|
||||
|
|
@ -575,6 +588,12 @@ BEGIN_PROTECTED
|
|||
END_PROTECTED
|
||||
}
|
||||
|
||||
void
|
||||
FillDialog::enhanced_fill_changed (int ef)
|
||||
{
|
||||
origin_le->setEnabled (ef != Qt::Checked);
|
||||
}
|
||||
|
||||
void
|
||||
FillDialog::fill_area_changed (int fa)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ struct LAY_PUBLIC FillParameters
|
|||
bool exclude_all_layers;
|
||||
std::vector<db::LayerProperties> exclude_layers;
|
||||
FillRegionMode fill_region_mode;
|
||||
db::DPoint fill_origin;
|
||||
db::Region fill_region;
|
||||
db::LayerProperties fill_region_layer;
|
||||
db::DVector exclude_distance;
|
||||
|
|
@ -81,6 +82,7 @@ public:
|
|||
|
||||
public slots:
|
||||
void fill_area_changed (int);
|
||||
void enhanced_fill_changed (int);
|
||||
void ok_pressed ();
|
||||
void choose_fc ();
|
||||
void choose_fc_2nd ();
|
||||
|
|
|
|||
|
|
@ -220,7 +220,7 @@ Finder::do_find (const db::Cell &cell, int level, const db::DCplxTrans &vp, cons
|
|||
&& (t * m_cell_box_convert (cell)).touches (m_scan_region)
|
||||
&& (mp_view->select_inside_pcells_mode () || !cell.is_proxy ())
|
||||
&& !mp_view->is_cell_hidden (cell.cell_index (), m_cv_index)
|
||||
&& !cell.is_ghost_cell ()) {
|
||||
&& !cell.is_real_ghost_cell ()) {
|
||||
|
||||
db::ICplxTrans it = t.inverted ();
|
||||
db::Box scan_box (it * m_scan_region);
|
||||
|
|
@ -846,7 +846,7 @@ InstFinder::reset_counter ()
|
|||
bool
|
||||
InstFinder::consider_cell (const db::Cell &cell) const
|
||||
{
|
||||
return cell.is_ghost_cell () ? m_consider_ghost_cells : m_consider_normal_cells;
|
||||
return cell.is_real_ghost_cell () ? m_consider_ghost_cells : m_consider_normal_cells;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -877,7 +877,7 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d
|
|||
// just consider the instances exactly at the last level of
|
||||
// hierarchy (this is where the boxes are drawn) or of cells that
|
||||
// are hidden.
|
||||
if (level == max_level () - 1 || inst_cell.is_proxy () || inst_cell.is_ghost_cell () || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) {
|
||||
if (level == max_level () - 1 || inst_cell.is_proxy () || inst_cell.is_real_ghost_cell () || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) {
|
||||
|
||||
db::box_convert <db::CellInst, false> bc (layout ());
|
||||
for (db::CellInstArray::iterator p = cell_inst.begin_touching (search_box, bc); ! p.at_end (); ++p) {
|
||||
|
|
@ -885,7 +885,7 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d
|
|||
checkpoint ();
|
||||
|
||||
db::Box ibox;
|
||||
if (! m_visible_layers || level == mp_view->get_max_hier_levels () - 1 || inst_cell.is_ghost_cell () || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) {
|
||||
if (! m_visible_layers || level == mp_view->get_max_hier_levels () - 1 || inst_cell.is_real_ghost_cell () || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) {
|
||||
ibox = inst_cell.bbox_with_empty ();
|
||||
} else if (inst_cell.bbox ().empty ()) {
|
||||
// empty cells cannot be found by visible layers, so we always select them here
|
||||
|
|
@ -954,7 +954,7 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d
|
|||
// just consider the instances exactly at the last level of
|
||||
// hierarchy (this is where the boxes are drawn) or if of cells that
|
||||
// are hidden.
|
||||
if (level == max_level () - 1 || inst_cell.is_proxy () || inst_cell.is_ghost_cell () || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) {
|
||||
if (level == max_level () - 1 || inst_cell.is_proxy () || inst_cell.is_real_ghost_cell () || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) {
|
||||
|
||||
db::box_convert <db::CellInst, false> bc (layout ());
|
||||
for (db::CellInstArray::iterator p = cell_inst.begin_touching (search_box, bc); ! p.at_end (); ++p) {
|
||||
|
|
@ -965,7 +965,7 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d
|
|||
double d = std::numeric_limits<double>::max ();
|
||||
|
||||
db::Box ibox;
|
||||
if (! m_visible_layers || level == mp_view->get_max_hier_levels () - 1 || inst_cell.is_ghost_cell () || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) {
|
||||
if (! m_visible_layers || level == mp_view->get_max_hier_levels () - 1 || inst_cell.is_real_ghost_cell () || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) {
|
||||
ibox = inst_cell.bbox_with_empty ();
|
||||
} else if (inst_cell.bbox ().empty ()) {
|
||||
// empty cells cannot be found by visible layers, so we always select them here
|
||||
|
|
|
|||
|
|
@ -269,9 +269,12 @@ MoveService::mouse_double_click_event (const db::DPoint &p, unsigned int buttons
|
|||
handle_click (p, buttons, false, 0);
|
||||
}
|
||||
|
||||
lay::SelectionService *selector = mp_view->selection_service ();
|
||||
if (selector) {
|
||||
return selector->mouse_double_click_event (p, buttons, prio);
|
||||
if (is_active ()) {
|
||||
// in move mode, a double click opens the properties dialog
|
||||
lay::SelectionService *selector = mp_view->selection_service ();
|
||||
if (selector) {
|
||||
return selector->mouse_double_click_event (p, buttons, prio);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -714,7 +714,7 @@ RedrawThreadWorker::setup (LayoutViewBase *view, RedrawThreadCanvas *canvas, con
|
|||
std::set <lay::LayoutViewBase::cell_index_type> &gc = m_ghost_cells [i];
|
||||
const db::Layout &ly = view->cellview (i)->layout ();
|
||||
for (auto c = ly.begin (); c != ly.end (); ++c) {
|
||||
if (c->is_ghost_cell ()) {
|
||||
if (c->is_real_ghost_cell ()) {
|
||||
gc.insert (c->cell_index ());
|
||||
}
|
||||
}
|
||||
|
|
@ -950,12 +950,12 @@ RedrawThreadWorker::draw_boxes_impl (bool drawing_context, db::cell_index_type c
|
|||
bbox_for_label = bbox;
|
||||
}
|
||||
|
||||
if (for_ghosts && cell.is_ghost_cell ()) {
|
||||
if (for_ghosts && cell.is_real_ghost_cell ()) {
|
||||
|
||||
// paint the box on this level
|
||||
draw_cell (drawing_context, level, trans, bbox, bbox_for_label, empty_cell, mp_layout->display_name (ci), opt_bitmap);
|
||||
|
||||
} else if (! for_ghosts && ! cell.is_ghost_cell () && (level == m_to_level || (m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (ci) != m_hidden_cells [m_cv_index].end ()))) {
|
||||
} else if (! for_ghosts && ! cell.is_real_ghost_cell () && (level == m_to_level || (m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (ci) != m_hidden_cells [m_cv_index].end ()))) {
|
||||
|
||||
// paint the box on this level
|
||||
draw_cell (drawing_context, level, trans, bbox, bbox_for_label, empty_cell, mp_layout->display_name (ci), opt_bitmap);
|
||||
|
|
@ -1186,12 +1186,12 @@ RedrawThreadWorker::draw_box_properties_impl (bool drawing_context, db::cell_ind
|
|||
|
||||
// small cell dropped
|
||||
|
||||
} else if (for_ghosts && cell.is_ghost_cell ()) {
|
||||
} else if (for_ghosts && cell.is_real_ghost_cell ()) {
|
||||
|
||||
// paint the box on this level
|
||||
draw_cell_properties (drawing_context, level, trans, bbox, prop_id);
|
||||
|
||||
} else if (! for_ghosts && ! cell.is_ghost_cell () && (level == m_to_level || (m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (ci) != m_hidden_cells [m_cv_index].end ()))) {
|
||||
} else if (! for_ghosts && ! cell.is_real_ghost_cell () && (level == m_to_level || (m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (ci) != m_hidden_cells [m_cv_index].end ()))) {
|
||||
|
||||
// paint the box on this level
|
||||
draw_cell_properties (drawing_context, level, trans, bbox, prop_id);
|
||||
|
|
@ -1317,7 +1317,7 @@ RedrawThreadWorker::any_shapes (db::cell_index_type cell_index, unsigned int lev
|
|||
|
||||
// Ghost cells are not drawn either
|
||||
const db::Cell &cell = mp_layout->cell (cell_index);
|
||||
if (cell.is_ghost_cell ()) {
|
||||
if (cell.is_real_ghost_cell ()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1359,7 +1359,7 @@ RedrawThreadWorker::any_cell_box (db::cell_index_type cell_index, unsigned int l
|
|||
|
||||
// ghost cells are also drawn
|
||||
const db::Cell &cell = mp_layout->cell (cell_index);
|
||||
if (cell.is_ghost_cell ()) {
|
||||
if (cell.is_real_ghost_cell ()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1400,7 +1400,7 @@ RedrawThreadWorker::any_text_shapes (db::cell_index_type cell_index, unsigned in
|
|||
|
||||
// Ghost cells are not drawn either
|
||||
const db::Cell &cell = mp_layout->cell (cell_index);
|
||||
if (cell.is_ghost_cell ()) {
|
||||
if (cell.is_real_ghost_cell ()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1503,7 +1503,7 @@ RedrawThreadWorker::draw_text_layer (bool drawing_context, db::cell_index_type c
|
|||
|
||||
} else if (! bbox.empty ()) {
|
||||
|
||||
bool hidden = cell.is_ghost_cell () || ((m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (ci) != m_hidden_cells [m_cv_index].end ()));
|
||||
bool hidden = cell.is_real_ghost_cell () || ((m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (ci) != m_hidden_cells [m_cv_index].end ()));
|
||||
bool need_to_dive = (level + 1 < m_to_level) && ! hidden;
|
||||
|
||||
db::Box cell_bbox = cell.bbox ();
|
||||
|
|
@ -1637,7 +1637,7 @@ RedrawThreadWorker::draw_text_layer (bool drawing_context, db::cell_index_type c
|
|||
++inst;
|
||||
|
||||
db::cell_index_type new_ci = cell_inst.object ().cell_index ();
|
||||
bool hidden = mp_layout->cell (new_ci).is_ghost_cell () || ((m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (new_ci) != m_hidden_cells [m_cv_index].end ()));
|
||||
bool hidden = mp_layout->cell (new_ci).is_real_ghost_cell () || ((m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (new_ci) != m_hidden_cells [m_cv_index].end ()));
|
||||
|
||||
db::Box cell_box = mp_layout->cell (new_ci).bbox (m_layer);
|
||||
if (! cell_box.empty () && ! hidden) {
|
||||
|
|
@ -1939,7 +1939,7 @@ RedrawThreadWorker::draw_layer_wo_cache (int from_level, int to_level, db::cell_
|
|||
++inst;
|
||||
|
||||
db::cell_index_type new_ci = cell_inst.object ().cell_index ();
|
||||
bool hidden = mp_layout->cell (new_ci).is_ghost_cell () || ((m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (new_ci) != m_hidden_cells [m_cv_index].end ()));
|
||||
bool hidden = mp_layout->cell (new_ci).is_real_ghost_cell () || ((m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (new_ci) != m_hidden_cells [m_cv_index].end ()));
|
||||
|
||||
db::Box new_cell_box = mp_layout->cell (new_ci).bbox (m_layer);
|
||||
if (! new_cell_box.empty () && ! hidden) {
|
||||
|
|
@ -2096,7 +2096,7 @@ RedrawThreadWorker::draw_layer (int from_level, int to_level, db::cell_index_typ
|
|||
db::Box cell_bbox = cell.bbox ();
|
||||
|
||||
// Nothing to draw
|
||||
if (bbox.empty () || cell.is_ghost_cell ()) {
|
||||
if (bbox.empty () || cell.is_real_ghost_cell ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ class shape_as_user_object
|
|||
: public db::DUserObjectBase
|
||||
{
|
||||
public:
|
||||
shape_as_user_object<Sh> (const Sh &sh)
|
||||
shape_as_user_object (const Sh &sh)
|
||||
: m_shape (sh)
|
||||
{ }
|
||||
|
||||
|
|
|
|||
|
|
@ -156,9 +156,13 @@ Class<lay::LayoutView> decl_LayoutView (decl_LayoutViewBase, "lay", "LayoutView"
|
|||
gsi::constructor ("new", &new_view, gsi::arg ("editable", false), gsi::arg ("manager", (db::Manager *) 0, "nil"), gsi::arg ("options", (unsigned int) 0),
|
||||
"@brief Creates a standalone view\n"
|
||||
"\n"
|
||||
"This constructor is for special purposes only. To create a view in the context of a main window, "
|
||||
"This constructor creates a non-GUI, standalone layout view. It is not attached to a Qt object and can be used for generating "
|
||||
"images through the screenshot method for example. To create a view in the context of a main window, "
|
||||
"use \\MainWindow#create_view and related methods.\n"
|
||||
"\n"
|
||||
"A standalone layout view has a default configuration that is different from a standard view - for example the "
|
||||
"color scheme is not initialized according to screen settings.\n"
|
||||
"\n"
|
||||
"@param editable True to make the view editable\n"
|
||||
"@param manager The \\Manager object to enable undo/redo\n"
|
||||
"@param options A combination of the values in the LV_... constants from \\LayoutViewBase\n"
|
||||
|
|
|
|||
|
|
@ -577,7 +577,7 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S
|
|||
|
||||
// don't write ghost cells unless they are not empty (any more)
|
||||
// also don't write proxy cells which are not employed
|
||||
if ((! cref.is_ghost_cell () || ! cref.empty ()) && (! cref.is_proxy () || ! cref.is_top ())) {
|
||||
if (! cref.is_real_ghost_cell () && (! cref.is_proxy () || ! cref.is_top ())) {
|
||||
|
||||
try {
|
||||
write_cell (layout, cref, layers, cell_set, sf, time_data);
|
||||
|
|
|
|||
|
|
@ -913,7 +913,7 @@ Writer::make_meta_data (const db::Cell *cell, stream::metaData::MetaData::Builde
|
|||
void
|
||||
Writer::write_cell (db::cell_index_type ci, kj::BufferedOutputStream &os)
|
||||
{
|
||||
bool needs_layout_view = ! mp_layout->cell (ci).is_ghost_cell ();
|
||||
bool needs_layout_view = ! mp_layout->cell (ci).is_real_ghost_cell ();
|
||||
bool needs_meta_data_view = mp_layout->begin_meta (ci) != mp_layout->end_meta (ci);
|
||||
|
||||
capnp::MallocMessageBuilder message;
|
||||
|
|
|
|||
|
|
@ -1322,21 +1322,37 @@ OASISReader::resolve_forward_references (db::PropertiesSet &properties)
|
|||
const tl::Variant &name = db::property_name (p->first);
|
||||
if (name.is_id ()) {
|
||||
|
||||
std::map <uint64_t, db::property_names_id_type>::iterator pf = m_propname_forward_references.find (name.to_id ());
|
||||
uint64_t name_id = name.to_id ();
|
||||
bool is_sprop = (name_id & 1) != 0;
|
||||
name_id >>= 1;
|
||||
|
||||
std::map <uint64_t, db::property_names_id_type>::iterator pf = m_propname_forward_references.find (name_id);
|
||||
if (pf != m_propname_forward_references.end ()) {
|
||||
|
||||
if (pf->second == m_s_gds_property_name_id) {
|
||||
if (pf->second == m_klayout_context_property_name_id) {
|
||||
|
||||
// NOTE: property names ID 0 is reserved for context strings
|
||||
new_props.insert (property_names_id_type (0), value);
|
||||
|
||||
} else if (! m_read_properties) {
|
||||
|
||||
// ignore other properties
|
||||
|
||||
} else if (pf->second == m_s_gds_property_name_id && is_sprop) {
|
||||
|
||||
// S_GDS_PROPERTY translation
|
||||
if (value.is_list () && value.get_list ().size () >= 2) {
|
||||
new_props.insert (value.get_list () [0], value.get_list () [1]);
|
||||
}
|
||||
|
||||
} else if (pf->second == m_klayout_context_property_name_id) {
|
||||
// NOTE: property names ID 0 is reserved for context strings
|
||||
new_props.insert (property_names_id_type (0), value);
|
||||
} else if (is_sprop && ! m_read_all_properties) {
|
||||
|
||||
// ignore system properties
|
||||
|
||||
} else {
|
||||
|
||||
new_props.insert (pf->second, value);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1395,10 +1411,27 @@ OASISReader::replace_forward_references_in_variant (tl::Variant &v)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
store_properties (db::PropertiesSet &properties, db::property_names_id_type name, const db::OASISReader::property_value_list &value)
|
||||
{
|
||||
if (value.size () == 0) {
|
||||
properties.insert (name, tl::Variant ());
|
||||
} else if (value.size () == 1) {
|
||||
properties.insert (name, tl::Variant (value [0]));
|
||||
} else if (value.size () > 1) {
|
||||
properties.insert (name, tl::Variant (value.begin (), value.end ()));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
OASISReader::store_last_properties (db::PropertiesSet &properties, bool ignore_special, bool with_context_props)
|
||||
{
|
||||
if (with_context_props && mm_last_property_name.get () == m_klayout_context_property_name_id) {
|
||||
const tl::Variant &name = db::property_name (mm_last_property_name.get ());
|
||||
if (name.is_id ()) {
|
||||
|
||||
store_properties (properties, mm_last_property_name.get (), mm_last_value_list.get ());
|
||||
|
||||
} else if (with_context_props && mm_last_property_name.get () == m_klayout_context_property_name_id) {
|
||||
|
||||
// Context properties are stored with a special property name ID of 0
|
||||
|
||||
|
|
@ -1422,12 +1455,10 @@ OASISReader::store_last_properties (db::PropertiesSet &properties, bool ignore_s
|
|||
// This is mode is used for cells and layouts so the standard properties do not appear as user properties.
|
||||
// For shapes we need to keep the special ones since they may be forward-references S_GDS_PROPERTY names.
|
||||
|
||||
} else if (mm_last_value_list.get ().size () == 0) {
|
||||
properties.insert (mm_last_property_name.get (), tl::Variant ());
|
||||
} else if (mm_last_value_list.get ().size () == 1) {
|
||||
properties.insert (mm_last_property_name.get (), tl::Variant (mm_last_value_list.get () [0]));
|
||||
} else if (mm_last_value_list.get ().size () > 1) {
|
||||
properties.insert (mm_last_property_name.get (), tl::Variant (mm_last_value_list.get ().begin (), mm_last_value_list.get ().end ()));
|
||||
} else {
|
||||
|
||||
store_properties (properties, mm_last_property_name.get (), mm_last_value_list.get ());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1510,7 +1541,7 @@ OASISReader::read_properties ()
|
|||
|
||||
std::map <uint64_t, std::string>::const_iterator cid = m_propnames.find (id);
|
||||
if (cid == m_propnames.end ()) {
|
||||
mm_last_property_name = db::property_names_id (tl::Variant (id, true /*dummy for id type*/));
|
||||
mm_last_property_name = db::property_names_id (tl::Variant ((id << 1) + uint64_t (is_sprop ? 1 : 0), true /*dummy for id type*/));
|
||||
m_propname_forward_references.insert (std::make_pair (id, db::property_names_id_type (0)));
|
||||
} else {
|
||||
mm_last_property_name = db::property_names_id (tl::Variant (cid->second));
|
||||
|
|
|
|||
|
|
@ -1443,12 +1443,6 @@ static bool must_write_cell (const db::Cell &cref)
|
|||
return ! cref.is_proxy () || ! cref.is_top ();
|
||||
}
|
||||
|
||||
static bool skip_cell_body (const db::Cell &cref)
|
||||
{
|
||||
// Skip cell bodies for ghost cells unless empty (they are not longer ghost cells in this case)
|
||||
return cref.is_ghost_cell () && cref.empty ();
|
||||
}
|
||||
|
||||
void
|
||||
OASISWriter::create_cell_nstrings (const db::Layout &layout, const std::set <db::cell_index_type> &cell_set)
|
||||
{
|
||||
|
|
@ -1670,7 +1664,7 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save
|
|||
mp_cell = &cref;
|
||||
|
||||
// skip cell body if the cell is not to be written
|
||||
if (skip_cell_body (cref)) {
|
||||
if (cref.is_real_ghost_cell ()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ static void set_oasis_read_all_properties (db::LoadLayoutOptions *options, bool
|
|||
options->get_options<db::OASISReaderOptions> ().read_all_properties = f;
|
||||
}
|
||||
|
||||
static int get_oasis_read_all_properties (const db::LoadLayoutOptions *options)
|
||||
static bool get_oasis_read_all_properties (const db::LoadLayoutOptions *options)
|
||||
{
|
||||
return options->get_options<db::OASISReaderOptions> ().read_all_properties;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1214,8 +1214,9 @@ property_setter_impl (int mid, PyObject *self, PyObject *value)
|
|||
|
||||
// check arguments (count and type)
|
||||
bool is_valid = (*m)->compatible_with_num_args (1);
|
||||
bool loose = (pass != 0); // loose in the second pass
|
||||
if (is_valid && ! test_arg (*(*m)->begin_arguments (), value, loose, loose)) {
|
||||
bool loose = (pass > 0); // loose in the second and third pass
|
||||
bool object_substitution = (pass > 1); // object substitution in the third pass
|
||||
if (is_valid && ! test_arg (*(*m)->begin_arguments (), value, loose, object_substitution)) {
|
||||
is_valid = false;
|
||||
}
|
||||
|
||||
|
|
@ -1228,7 +1229,7 @@ property_setter_impl (int mid, PyObject *self, PyObject *value)
|
|||
|
||||
++pass;
|
||||
|
||||
} while (! meth && pass < 2);
|
||||
} while (! meth && pass < 3);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1676,6 +1676,15 @@ Class<rdb::Database> decl_ReportDatabase ("rdb", "ReportDatabase",
|
|||
"\n"
|
||||
"This method has been added in version 0.29.1."
|
||||
) +
|
||||
gsi::method ("merge", &rdb::Database::merge, gsi::arg ("other"),
|
||||
"@brief Merges the other database with this one\n"
|
||||
"This method will merge the other database with this one. The other database needs to have the same top cell than\n"
|
||||
"this database. In the merge step, identical cells and categories will be identified and missing cells or categories\n"
|
||||
"will be created in this database. After that, all items will be transferred from the other database into this one\n"
|
||||
"and will be associated with cells and categories from this database.\n"
|
||||
"\n"
|
||||
"This method has been added in version 0.30.7."
|
||||
) +
|
||||
gsi::method ("is_modified?", &rdb::Database::is_modified,
|
||||
"@brief Returns a value indicating whether the database has been modified\n"
|
||||
) +
|
||||
|
|
|
|||
|
|
@ -958,11 +958,18 @@ Tags::tag (id_type id)
|
|||
void
|
||||
Tags::import_tag (const Tag &t)
|
||||
{
|
||||
Tag &tt = tag (t.name (), t.is_user_tag ());
|
||||
tt.set_description (t.description ());
|
||||
import_tag_with_ref (t);
|
||||
}
|
||||
|
||||
bool
|
||||
Tag &
|
||||
Tags::import_tag_with_ref (const Tag &t)
|
||||
{
|
||||
Tag &tt = tag (t.name (), t.is_user_tag ());
|
||||
tt.set_description (t.description ());
|
||||
return tt;
|
||||
}
|
||||
|
||||
bool
|
||||
Tags::has_tag (const std::string &name, bool user_tag) const
|
||||
{
|
||||
return m_ids_for_names.find (std::make_pair (name, user_tag)) != m_ids_for_names.end ();
|
||||
|
|
@ -1314,6 +1321,13 @@ Database::import_tags (const Tags &tags)
|
|||
}
|
||||
}
|
||||
|
||||
Tag &
|
||||
Database::import_tag (const Tag &tag)
|
||||
{
|
||||
set_modified ();
|
||||
return m_tags.import_tag_with_ref (tag);
|
||||
}
|
||||
|
||||
void
|
||||
Database::import_categories (Categories *categories)
|
||||
{
|
||||
|
|
@ -1880,41 +1894,52 @@ namespace
|
|||
};
|
||||
}
|
||||
|
||||
static void map_category (const rdb::Category &cat, const rdb::Database &db, std::map<id_type, id_type> &cat2cat)
|
||||
static void map_category (const rdb::Category &cat, rdb::Database &db, std::map<id_type, id_type> &cat2cat, std::map<id_type, id_type> &rev_cat2cat, bool create_missing, rdb::Category *parent)
|
||||
{
|
||||
const rdb::Category *this_cat = db.category_by_name (cat.path ());
|
||||
rdb::Category *this_cat = db.category_by_name_non_const (cat.path ());
|
||||
if (! this_cat && create_missing) {
|
||||
this_cat = db.create_category (parent, cat.name ());
|
||||
this_cat->set_description (cat.description ());
|
||||
}
|
||||
if (this_cat) {
|
||||
cat2cat.insert (std::make_pair (this_cat->id (), cat.id ()));
|
||||
rev_cat2cat.insert (std::make_pair (cat.id (), this_cat->id ()));
|
||||
}
|
||||
|
||||
for (auto c = cat.sub_categories ().begin (); c != cat.sub_categories ().end (); ++c) {
|
||||
map_category (*c, db, cat2cat);
|
||||
map_category (*c, db, cat2cat, rev_cat2cat, create_missing, this_cat);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Database::apply (const rdb::Database &other)
|
||||
static void map_databases (rdb::Database &self, const rdb::Database &other,
|
||||
std::map<id_type, id_type> &cell2cell,
|
||||
std::map<id_type, id_type> &rev_cell2cell,
|
||||
std::map<id_type, id_type> &cat2cat,
|
||||
std::map<id_type, id_type> &rev_cat2cat,
|
||||
std::map<id_type, id_type> &tag2tag,
|
||||
std::map<id_type, id_type> &rev_tag2tag,
|
||||
bool create_missing)
|
||||
{
|
||||
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 ());
|
||||
rdb::Cell *this_cell = self.cell_by_qname_non_const (c->qname ());
|
||||
if (! this_cell && create_missing) {
|
||||
this_cell = self.create_cell (c->name (), c->variant (), c->layout_name ());
|
||||
this_cell->import_references (c->references ());
|
||||
}
|
||||
if (this_cell) {
|
||||
cell2cell.insert (std::make_pair (this_cell->id (), c->id ()));
|
||||
rev_cell2cell.insert (std::make_pair (c->id (), this_cell->id ()));
|
||||
}
|
||||
}
|
||||
|
||||
for (auto c = other.categories ().begin (); c != other.categories ().end (); ++c) {
|
||||
map_category (*c, *this, cat2cat);
|
||||
map_category (*c, self, cat2cat, rev_cat2cat, create_missing, 0);
|
||||
}
|
||||
|
||||
std::map<std::string, id_type> tags_by_name;
|
||||
for (auto c = tags ().begin_tags (); c != tags ().end_tags (); ++c) {
|
||||
for (auto c = self.tags ().begin_tags (); c != self.tags ().end_tags (); ++c) {
|
||||
tags_by_name.insert (std::make_pair (c->name (), c->id ()));
|
||||
}
|
||||
|
||||
|
|
@ -1923,8 +1948,25 @@ Database::apply (const rdb::Database &other)
|
|||
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));
|
||||
} else if (create_missing) {
|
||||
auto tt = self.import_tag (*c);
|
||||
tag2tag.insert (std::make_pair (tt.id (), c->id ()));
|
||||
rev_tag2tag.insert (std::make_pair (c->id (), tt.id ()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Database::apply (const rdb::Database &other)
|
||||
{
|
||||
std::map<id_type, id_type> cell2cell;
|
||||
std::map<id_type, id_type> rev_cell2cell;
|
||||
std::map<id_type, id_type> cat2cat;
|
||||
std::map<id_type, id_type> rev_cat2cat;
|
||||
std::map<id_type, id_type> tag2tag;
|
||||
std::map<id_type, id_type> rev_tag2tag;
|
||||
|
||||
map_databases (*this, other, cell2cell, rev_cell2cell, cat2cat, rev_cat2cat, tag2tag, rev_tag2tag, false);
|
||||
|
||||
std::map<std::pair<id_type, id_type>, ValueMapEntry> value_map;
|
||||
|
||||
|
|
@ -1963,6 +2005,50 @@ Database::apply (const rdb::Database &other)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
Database::merge (const Database &other)
|
||||
{
|
||||
if (top_cell_name () != other.top_cell_name ()) {
|
||||
throw tl::Exception (tl::to_string (tr ("Merging of RDB databases requires identical top cell names")));
|
||||
}
|
||||
|
||||
std::map<id_type, id_type> cell2cell;
|
||||
std::map<id_type, id_type> rev_cell2cell;
|
||||
std::map<id_type, id_type> cat2cat;
|
||||
std::map<id_type, id_type> rev_cat2cat;
|
||||
std::map<id_type, id_type> tag2tag;
|
||||
std::map<id_type, id_type> rev_tag2tag;
|
||||
|
||||
map_databases (*this, other, cell2cell, rev_cell2cell, cat2cat, rev_cat2cat, tag2tag, rev_tag2tag, true);
|
||||
|
||||
for (Items::const_iterator i = other.items ().begin (); i != other.items ().end (); ++i) {
|
||||
|
||||
auto icell = rev_cell2cell.find (i->cell_id ());
|
||||
if (icell == rev_cell2cell.end ()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto icat = rev_cat2cat.find (i->category_id ());
|
||||
if (icat == rev_cat2cat.end ()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
rdb::Item *this_item = create_item (icell->second, icat->second);
|
||||
|
||||
this_item->set_values (i->values ());
|
||||
this_item->set_multiplicity (i->multiplicity ());
|
||||
this_item->set_comment (i->comment ());
|
||||
this_item->set_image_str (i->image_str ());
|
||||
|
||||
for (auto tt = rev_tag2tag.begin (); tt != rev_tag2tag.end (); ++tt) {
|
||||
if (i->has_tag (tt->first)) {
|
||||
this_item->add_tag (tt->second);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1987,6 +1987,11 @@ public:
|
|||
*/
|
||||
void import_tag (const Tag &tag);
|
||||
|
||||
/**
|
||||
* @brief Import a tag and returns the reference for the new tag
|
||||
*/
|
||||
Tag &import_tag_with_ref (const Tag &tag);
|
||||
|
||||
/**
|
||||
* @brief Clear the collection of tags
|
||||
*/
|
||||
|
|
@ -2143,6 +2148,11 @@ public:
|
|||
*/
|
||||
void import_tags (const Tags &tags);
|
||||
|
||||
/**
|
||||
* @brief Import a tag
|
||||
*/
|
||||
Tag &import_tag (const Tag &tag);
|
||||
|
||||
/**
|
||||
* @brief Get the reference to the categories collection (const version)
|
||||
*/
|
||||
|
|
@ -2478,6 +2488,17 @@ public:
|
|||
*/
|
||||
void apply (const rdb::Database &other);
|
||||
|
||||
/**
|
||||
* @brief Merges another database to this one
|
||||
*
|
||||
* Merging requires the other database to have the same top cell.
|
||||
* Merging involves:
|
||||
* * categories present in other, but not in *this will be created (based on category name and parent category)
|
||||
* * cells present in other, but not in *this will be created (based on cell name and variant)
|
||||
* * items will be transferred from other to *this based on cell and category
|
||||
*/
|
||||
void merge (const rdb::Database &other);
|
||||
|
||||
/**
|
||||
* @brief Scans a layout into this RDB
|
||||
*
|
||||
|
|
|
|||
|
|
@ -822,3 +822,250 @@ TEST(13_ApplyIgnoreUnknownTag)
|
|||
EXPECT_EQ (i1->tag_str (), "tag2");
|
||||
}
|
||||
|
||||
TEST(20_MergeBasic)
|
||||
{
|
||||
rdb::Database db1;
|
||||
db1.set_top_cell_name ("A");
|
||||
|
||||
rdb::Database db2;
|
||||
db1.set_top_cell_name ("B");
|
||||
|
||||
try {
|
||||
// can't merge DB's with different top cell names
|
||||
db1.merge (db2);
|
||||
EXPECT_EQ (0, 1);
|
||||
} catch (...) { }
|
||||
|
||||
db1.set_top_cell_name ("TOP");
|
||||
db2.set_top_cell_name ("TOP");
|
||||
db1.merge (db2);
|
||||
|
||||
{
|
||||
rdb::Cell *cell = db2.create_cell ("A", "VAR", "ALAY");
|
||||
|
||||
rdb::Category *pcat = db2.create_category ("PCAT");
|
||||
rdb::Category *cat = db2.create_category (pcat, "CAT");
|
||||
cat->set_description ("A child category");
|
||||
|
||||
// create two tags
|
||||
/*auto t1_id =*/ db2.tags ().tag ("T1").id ();
|
||||
auto t2_id = db2.tags ().tag ("T2", true).id ();
|
||||
|
||||
rdb::Item *item = db2.create_item (cell->id (), cat->id ());
|
||||
item->set_comment ("Comment");
|
||||
item->add_tag (t2_id);
|
||||
item->set_image_str ("%nonsense%");
|
||||
item->set_multiplicity (42);
|
||||
item->add_value (db::DBox (0, 0, 1.0, 1.5));
|
||||
item->add_value (42.0);
|
||||
}
|
||||
|
||||
db1.merge (db2);
|
||||
|
||||
auto c = db1.cells ().begin ();
|
||||
tl_assert (c != db1.cells ().end ());
|
||||
const rdb::Cell *cell = c.operator-> ();
|
||||
++c;
|
||||
EXPECT_EQ (c == db1.cells ().end (), true);
|
||||
EXPECT_EQ (cell->name (), "A");
|
||||
EXPECT_EQ (cell->variant (), "VAR");
|
||||
EXPECT_EQ (cell->layout_name (), "ALAY");
|
||||
|
||||
const rdb::Category *cat = db1.category_by_name ("PCAT.CAT");
|
||||
tl_assert (cat != 0);
|
||||
EXPECT_EQ (cat->name (), "CAT");
|
||||
EXPECT_EQ (cat->path (), "PCAT.CAT");
|
||||
EXPECT_EQ (cat->description (), "A child category");
|
||||
EXPECT_EQ (cat->num_items (), size_t (1));
|
||||
|
||||
auto i = db1.items ().begin ();
|
||||
tl_assert (i != db1.items ().end ());
|
||||
const rdb::Item *item = i.operator-> ();
|
||||
++i;
|
||||
EXPECT_EQ (i == db1.items ().end (), true);
|
||||
EXPECT_EQ (item->category_id (), cat->id ());
|
||||
EXPECT_EQ (item->cell_id (), cell->id ());
|
||||
EXPECT_EQ (item->comment (), "Comment");
|
||||
EXPECT_EQ (item->multiplicity (), size_t (42));
|
||||
EXPECT_EQ (item->has_image (), true);
|
||||
EXPECT_EQ (item->image_str (), "%nonsense%")
|
||||
EXPECT_EQ (item->values ().to_string (&db1), "box: (0,0;1,1.5);float: 42");
|
||||
EXPECT_EQ (item->tag_str (), "#T2");
|
||||
}
|
||||
|
||||
TEST(21_MergeCategories)
|
||||
{
|
||||
rdb::Database db1;
|
||||
db1.set_top_cell_name ("TOP");
|
||||
|
||||
rdb::Database db2;
|
||||
db2.set_top_cell_name ("TOP");
|
||||
|
||||
{
|
||||
rdb::Category *pcat = db1.create_category ("PCAT");
|
||||
pcat->set_description ("db1");
|
||||
rdb::Category *cat = db1.create_category (pcat, "CAT");
|
||||
cat->set_description ("db1");
|
||||
}
|
||||
|
||||
{
|
||||
rdb::Category *pcat = db2.create_category ("PCAT");
|
||||
pcat->set_description ("db2a");
|
||||
rdb::Category *cat2 = db2.create_category (pcat, "CAT2");
|
||||
cat2->set_description ("db2a");
|
||||
|
||||
rdb::Category *pcat2 = db2.create_category ("PCAT2");
|
||||
pcat2->set_description ("db2b");
|
||||
rdb::Category *cat3 = db2.create_category (pcat2, "CAT3");
|
||||
cat3->set_description ("db2b");
|
||||
}
|
||||
|
||||
db1.merge (db2);
|
||||
|
||||
const rdb::Category *cat;
|
||||
|
||||
cat = db1.category_by_name ("PCAT");
|
||||
tl_assert (cat != 0);
|
||||
EXPECT_EQ (cat->name (), "PCAT");
|
||||
EXPECT_EQ (cat->description (), "db1");
|
||||
|
||||
cat = db1.category_by_name ("PCAT2");
|
||||
tl_assert (cat != 0);
|
||||
EXPECT_EQ (cat->name (), "PCAT2");
|
||||
EXPECT_EQ (cat->description (), "db2b");
|
||||
|
||||
cat = db1.category_by_name ("PCAT.CAT");
|
||||
tl_assert (cat != 0);
|
||||
EXPECT_EQ (cat->name (), "CAT");
|
||||
EXPECT_EQ (cat->description (), "db1");
|
||||
|
||||
cat = db1.category_by_name ("PCAT.CAT2");
|
||||
tl_assert (cat != 0);
|
||||
EXPECT_EQ (cat->name (), "CAT2");
|
||||
EXPECT_EQ (cat->description (), "db2a");
|
||||
|
||||
cat = db1.category_by_name ("PCAT2.CAT3");
|
||||
tl_assert (cat != 0);
|
||||
EXPECT_EQ (cat->name (), "CAT3");
|
||||
EXPECT_EQ (cat->description (), "db2b");
|
||||
|
||||
int ncat = 0;
|
||||
for (auto c = db1.categories ().begin (); c != db1.categories ().end (); ++c) {
|
||||
++ncat;
|
||||
for (auto s = c->sub_categories ().begin (); s != c->sub_categories ().end (); ++s) {
|
||||
++ncat;
|
||||
}
|
||||
}
|
||||
EXPECT_EQ (ncat, 5);
|
||||
}
|
||||
|
||||
TEST(22_MergeCells)
|
||||
{
|
||||
rdb::Database db1;
|
||||
db1.set_top_cell_name ("TOP");
|
||||
|
||||
rdb::Database db2;
|
||||
db2.set_top_cell_name ("TOP");
|
||||
|
||||
{
|
||||
rdb::Cell *parent;
|
||||
parent = db1.create_cell ("TOP");
|
||||
|
||||
rdb::Cell *cell;
|
||||
cell = db1.create_cell ("A");
|
||||
cell->references ().insert (rdb::Reference (db::DCplxTrans (db::DVector (1.0, 2.0)), parent->id ()));
|
||||
cell = db1.create_cell ("A", "VAR1", "ALAY");
|
||||
cell->references ().insert (rdb::Reference (db::DCplxTrans (db::DVector (1.0, -2.0)), parent->id ()));
|
||||
}
|
||||
|
||||
{
|
||||
rdb::Cell *parent;
|
||||
parent = db2.create_cell ("TOP");
|
||||
|
||||
rdb::Cell *cell;
|
||||
cell = db2.create_cell ("B");
|
||||
cell->references ().insert (rdb::Reference (db::DCplxTrans (db::DVector (1.0, 0.0)), parent->id ()));
|
||||
cell = db2.create_cell ("A");
|
||||
cell->references ().insert (rdb::Reference (db::DCplxTrans (db::DVector (1.0, 2.5)), parent->id ())); // reference not taken!
|
||||
cell = db2.create_cell ("A", "VAR2", "ALAY");
|
||||
cell->references ().insert (rdb::Reference (db::DCplxTrans (db::DVector (1.0, -1.0)), parent->id ()));
|
||||
}
|
||||
|
||||
db1.merge (db2);
|
||||
|
||||
std::set<std::string> cells;
|
||||
for (auto c = db1.cells ().begin (); c != db1.cells ().end (); ++c) {
|
||||
if (c->references ().begin () != c->references ().end ()) {
|
||||
cells.insert (c->qname () + "[" + c->references ().begin ()->trans_str () + "]");
|
||||
} else {
|
||||
cells.insert (c->qname ());
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_EQ (tl::join (cells.begin (), cells.end (), ";"), "A:1[r0 *1 1,2];A:VAR1[r0 *1 1,-2];A:VAR2[r0 *1 1,-1];B[r0 *1 1,0];TOP");
|
||||
}
|
||||
|
||||
TEST(23_MergeTags)
|
||||
{
|
||||
rdb::Database db1;
|
||||
db1.set_top_cell_name ("TOP");
|
||||
|
||||
rdb::Database db2;
|
||||
db2.set_top_cell_name ("TOP");
|
||||
|
||||
db1.tags ().tag ("T1");
|
||||
db1.tags ().tag ("T2");
|
||||
|
||||
db2.tags ().tag ("T1");
|
||||
db2.tags ().tag ("T3", true);
|
||||
|
||||
db1.merge (db2);
|
||||
|
||||
std::set<std::string> tags;
|
||||
for (auto t = db1.tags ().begin_tags (); t != db1.tags ().end_tags (); ++t) {
|
||||
tags.insert (t->is_user_tag () ? "#" + t->name () : t->name ());
|
||||
}
|
||||
EXPECT_EQ (tl::join (tags.begin (), tags.end (), ";"), "#T3;T1;T2");
|
||||
}
|
||||
|
||||
TEST(24_MergeItems)
|
||||
{
|
||||
rdb::Database db1;
|
||||
db1.set_top_cell_name ("TOP");
|
||||
|
||||
rdb::Database db2;
|
||||
db2.set_top_cell_name ("TOP");
|
||||
|
||||
{
|
||||
rdb::Cell *cell = db1.create_cell ("TOP");
|
||||
rdb::Category *cat1 = db1.create_category ("CAT1");
|
||||
rdb::Category *cat2 = db1.create_category ("CAT2");
|
||||
|
||||
rdb::Item *item;
|
||||
item = db1.create_item (cell->id (), cat1->id ());
|
||||
item->set_comment ("db1a");
|
||||
item = db1.create_item (cell->id (), cat2->id ());
|
||||
item->set_comment ("db1b");
|
||||
}
|
||||
|
||||
{
|
||||
rdb::Cell *cell = db2.create_cell ("TOP");
|
||||
rdb::Category *cat1 = db2.create_category ("CAT1");
|
||||
rdb::Category *cat3 = db2.create_category ("CAT3");
|
||||
|
||||
rdb::Item *item;
|
||||
item = db2.create_item (cell->id (), cat1->id ());
|
||||
item->set_comment ("db2a");
|
||||
item = db2.create_item (cell->id (), cat3->id ());
|
||||
item->set_comment ("db2b");
|
||||
}
|
||||
|
||||
db1.merge (db2);
|
||||
|
||||
std::set<std::string> items;
|
||||
for (auto i = db1.items ().begin (); i != db1.items ().end (); ++i) {
|
||||
const rdb::Item *item = i.operator-> ();
|
||||
items.insert (item->cell_qname () + ":" + item->category_name () + "=" + item->comment ());
|
||||
}
|
||||
EXPECT_EQ (tl::join (items.begin (), items.end (), ";"), "TOP:CAT1=db1a;TOP:CAT1=db2a;TOP:CAT2=db1b;TOP:CAT3=db2b");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,203 +0,0 @@
|
|||
//----------------------------------------------------------------------------
|
||||
// This is free and unencumbered software released into the public domain.
|
||||
//
|
||||
// Anyone is free to copy, modify, publish, use, compile, sell, or distribute
|
||||
// this software, either in source code form or as a compiled binary, for any
|
||||
// purpose, commercial or non-commercial, and by any means.
|
||||
//
|
||||
// In jurisdictions that recognize copyright laws, the author or authors of
|
||||
// this software dedicate any and all copyright interest in the software to the
|
||||
// public domain. We make this dedication for the benefit of the public at
|
||||
// large and to the detriment of our heirs and successors. We intend this
|
||||
// dedication to be an overt act of relinquishment in perpetuity of all present
|
||||
// and future rights to this software under copyright law.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// For more information, please refer to <http://unlicense.org/>
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef ATOMIC_ATOMIC_H_
|
||||
#define ATOMIC_ATOMIC_H_
|
||||
|
||||
// Macro for disallowing copying of an object.
|
||||
#if __cplusplus >= 201103L
|
||||
#define ATOMIC_DISALLOW_COPY(T) \
|
||||
T(const T&) = delete; \
|
||||
T& operator=(const T&) = delete;
|
||||
#else
|
||||
#define ATOMIC_DISALLOW_COPY(T) \
|
||||
T(const T&); \
|
||||
T& operator=(const T&);
|
||||
#endif
|
||||
|
||||
// A portable static assert.
|
||||
#if __cplusplus >= 201103L
|
||||
#define ATOMIC_STATIC_ASSERT(condition, message) \
|
||||
static_assert((condition), message)
|
||||
#else
|
||||
// Based on: http://stackoverflow.com/a/809465/5778708
|
||||
#define ATOMIC_STATIC_ASSERT(condition, message) \
|
||||
_impl_STATIC_ASSERT_LINE(condition, __LINE__)
|
||||
#define _impl_PASTE(a, b) a##b
|
||||
#ifdef __GNUC__
|
||||
#define _impl_UNUSED __attribute__((__unused__))
|
||||
#else
|
||||
#define _impl_UNUSED
|
||||
#endif
|
||||
#define _impl_STATIC_ASSERT_LINE(condition, line) \
|
||||
typedef char _impl_PASTE( \
|
||||
STATIC_ASSERT_failed_, \
|
||||
line)[(2 * static_cast<int>(!!(condition))) - 1] _impl_UNUSED
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__) || defined(__xlc__)
|
||||
#define ATOMIC_USE_GCC_INTRINSICS
|
||||
#elif defined(_MSC_VER)
|
||||
#define ATOMIC_USE_MSVC_INTRINSICS
|
||||
#include "atomic_msvc.h"
|
||||
#elif __cplusplus >= 201103L
|
||||
#define ATOMIC_USE_CPP11_ATOMIC
|
||||
#include <atomic>
|
||||
#else
|
||||
#error Unsupported compiler / system.
|
||||
#endif
|
||||
|
||||
namespace atomic {
|
||||
template <typename T>
|
||||
class atomic {
|
||||
public:
|
||||
ATOMIC_STATIC_ASSERT(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 ||
|
||||
sizeof(T) == 8,
|
||||
"Only types of size 1, 2, 4 or 8 are supported");
|
||||
|
||||
atomic() : value_(static_cast<T>(0)) {}
|
||||
|
||||
explicit atomic(const T value) : value_(value) {}
|
||||
|
||||
/// @brief Performs an atomic increment operation (value + 1).
|
||||
/// @returns The new value of the atomic object.
|
||||
T operator++() {
|
||||
#if defined(ATOMIC_USE_GCC_INTRINSICS)
|
||||
return __atomic_add_fetch(&value_, 1, __ATOMIC_SEQ_CST);
|
||||
#elif defined(ATOMIC_USE_MSVC_INTRINSICS)
|
||||
return msvc::interlocked<T>::increment(&value_);
|
||||
#else
|
||||
return ++value_;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// @brief Performs an atomic decrement operation (value - 1).
|
||||
/// @returns The new value of the atomic object.
|
||||
T operator--() {
|
||||
#if defined(ATOMIC_USE_GCC_INTRINSICS)
|
||||
return __atomic_sub_fetch(&value_, 1, __ATOMIC_SEQ_CST);
|
||||
#elif defined(ATOMIC_USE_MSVC_INTRINSICS)
|
||||
return msvc::interlocked<T>::decrement(&value_);
|
||||
#else
|
||||
return --value_;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// @brief Performs an atomic compare-and-swap (CAS) operation.
|
||||
///
|
||||
/// The value of the atomic object is only updated to the new value if the
|
||||
/// old value of the atomic object matches @c expected_val.
|
||||
///
|
||||
/// @param expected_val The expected value of the atomic object.
|
||||
/// @param new_val The new value to write to the atomic object.
|
||||
/// @returns True if new_value was written to the atomic object.
|
||||
bool compare_exchange(const T expected_val, const T new_val) {
|
||||
#if defined(ATOMIC_USE_GCC_INTRINSICS)
|
||||
T e = expected_val;
|
||||
return __atomic_compare_exchange_n(
|
||||
&value_, &e, new_val, true, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
|
||||
#elif defined(ATOMIC_USE_MSVC_INTRINSICS)
|
||||
const T old_val =
|
||||
msvc::interlocked<T>::compare_exchange(&value_, new_val, expected_val);
|
||||
return (old_val == expected_val);
|
||||
#else
|
||||
T e = expected_val;
|
||||
return value_.compare_exchange_weak(e, new_val);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// @brief Performs an atomic set operation.
|
||||
///
|
||||
/// The value of the atomic object is unconditionally updated to the new
|
||||
/// value.
|
||||
///
|
||||
/// @param new_val The new value to write to the atomic object.
|
||||
void store(const T new_val) {
|
||||
#if defined(ATOMIC_USE_GCC_INTRINSICS)
|
||||
__atomic_store_n(&value_, new_val, __ATOMIC_SEQ_CST);
|
||||
#elif defined(ATOMIC_USE_MSVC_INTRINSICS)
|
||||
(void)msvc::interlocked<T>::exchange(&value_, new_val);
|
||||
#else
|
||||
value_.store(new_val);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// @returns the current value of the atomic object.
|
||||
/// @note Be careful about how this is used, since any operations on the
|
||||
/// returned value are inherently non-atomic.
|
||||
T load() const {
|
||||
#if defined(ATOMIC_USE_GCC_INTRINSICS)
|
||||
return __atomic_load_n(&value_, __ATOMIC_SEQ_CST);
|
||||
#elif defined(ATOMIC_USE_MSVC_INTRINSICS)
|
||||
// TODO(m): Is there a better solution for MSVC?
|
||||
return value_;
|
||||
#else
|
||||
return value_;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// @brief Performs an atomic exchange operation.
|
||||
///
|
||||
/// The value of the atomic object is unconditionally updated to the new
|
||||
/// value, and the old value is returned.
|
||||
///
|
||||
/// @param new_val The new value to write to the atomic object.
|
||||
/// @returns the old value.
|
||||
T exchange(const T new_val) {
|
||||
#if defined(ATOMIC_USE_GCC_INTRINSICS)
|
||||
return __atomic_exchange_n(&value_, new_val, __ATOMIC_SEQ_CST);
|
||||
#elif defined(ATOMIC_USE_MSVC_INTRINSICS)
|
||||
return msvc::interlocked<T>::exchange(&value_, new_val);
|
||||
#else
|
||||
return value_.exchange(new_val);
|
||||
#endif
|
||||
}
|
||||
|
||||
T operator=(const T new_value) {
|
||||
store(new_value);
|
||||
return new_value;
|
||||
}
|
||||
|
||||
operator T() const {
|
||||
return load();
|
||||
}
|
||||
|
||||
private:
|
||||
#if defined(ATOMIC_USE_GCC_INTRINSICS) || defined(ATOMIC_USE_MSVC_INTRINSICS)
|
||||
volatile T value_;
|
||||
#else
|
||||
std::atomic<T> value_;
|
||||
#endif
|
||||
|
||||
ATOMIC_DISALLOW_COPY(atomic)
|
||||
};
|
||||
|
||||
} // namespace atomic
|
||||
|
||||
// Undef temporary defines.
|
||||
#undef ATOMIC_USE_GCC_INTRINSICS
|
||||
#undef ATOMIC_USE_MSVC_INTRINSICS
|
||||
#undef ATOMIC_USE_CPP11_ATOMIC
|
||||
|
||||
#endif // ATOMIC_ATOMIC_H_
|
||||
|
|
@ -1,238 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// This is free and unencumbered software released into the public domain.
|
||||
//
|
||||
// Anyone is free to copy, modify, publish, use, compile, sell, or distribute
|
||||
// this software, either in source code form or as a compiled binary, for any
|
||||
// purpose, commercial or non-commercial, and by any means.
|
||||
//
|
||||
// In jurisdictions that recognize copyright laws, the author or authors of
|
||||
// this software dedicate any and all copyright interest in the software to the
|
||||
// public domain. We make this dedication for the benefit of the public at
|
||||
// large and to the detriment of our heirs and successors. We intend this
|
||||
// dedication to be an overt act of relinquishment in perpetuity of all present
|
||||
// and future rights to this software under copyright law.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// For more information, please refer to <http://unlicense.org/>
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef ATOMIC_ATOMIC_MSVC_H_
|
||||
#define ATOMIC_ATOMIC_MSVC_H_
|
||||
|
||||
// Define which functions we need (don't include <intrin.h>).
|
||||
extern "C" {
|
||||
short _InterlockedIncrement16(short volatile*);
|
||||
long _InterlockedIncrement(long volatile*);
|
||||
__int64 _InterlockedIncrement64(__int64 volatile*);
|
||||
|
||||
short _InterlockedDecrement16(short volatile*);
|
||||
long _InterlockedDecrement(long volatile*);
|
||||
__int64 _InterlockedDecrement64(__int64 volatile*);
|
||||
|
||||
char _InterlockedExchange8(char volatile*, char);
|
||||
short _InterlockedExchange16(short volatile*, short);
|
||||
long __cdecl _InterlockedExchange(long volatile*, long);
|
||||
__int64 _InterlockedExchange64(__int64 volatile*, __int64);
|
||||
|
||||
char _InterlockedCompareExchange8(char volatile*, char, char);
|
||||
short _InterlockedCompareExchange16(short volatile*, short, short);
|
||||
long __cdecl _InterlockedCompareExchange(long volatile*, long, long);
|
||||
__int64 _InterlockedCompareExchange64(__int64 volatile*, __int64, __int64);
|
||||
};
|
||||
|
||||
// Define which functions we want to use as inline intriniscs.
|
||||
#pragma intrinsic(_InterlockedIncrement)
|
||||
#pragma intrinsic(_InterlockedIncrement16)
|
||||
|
||||
#pragma intrinsic(_InterlockedDecrement)
|
||||
#pragma intrinsic(_InterlockedDecrement16)
|
||||
|
||||
#pragma intrinsic(_InterlockedCompareExchange)
|
||||
#pragma intrinsic(_InterlockedCompareExchange8)
|
||||
#pragma intrinsic(_InterlockedCompareExchange16)
|
||||
|
||||
#pragma intrinsic(_InterlockedExchange)
|
||||
#pragma intrinsic(_InterlockedExchange8)
|
||||
#pragma intrinsic(_InterlockedExchange16)
|
||||
|
||||
#if defined(_M_X64)
|
||||
#pragma intrinsic(_InterlockedIncrement64)
|
||||
#pragma intrinsic(_InterlockedDecrement64)
|
||||
#pragma intrinsic(_InterlockedCompareExchange64)
|
||||
#pragma intrinsic(_InterlockedExchange64)
|
||||
#endif // _M_X64
|
||||
|
||||
namespace atomic {
|
||||
namespace msvc {
|
||||
template <typename T, size_t N = sizeof(T)>
|
||||
struct interlocked {
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct interlocked<T, 1> {
|
||||
static inline T increment(T volatile* x) {
|
||||
// There's no _InterlockedIncrement8().
|
||||
char old_val, new_val;
|
||||
do {
|
||||
old_val = static_cast<char>(*x);
|
||||
new_val = old_val + static_cast<char>(1);
|
||||
} while (_InterlockedCompareExchange8(reinterpret_cast<volatile char*>(x),
|
||||
new_val,
|
||||
old_val) != old_val);
|
||||
return static_cast<T>(new_val);
|
||||
}
|
||||
|
||||
static inline T decrement(T volatile* x) {
|
||||
// There's no _InterlockedDecrement8().
|
||||
char old_val, new_val;
|
||||
do {
|
||||
old_val = static_cast<char>(*x);
|
||||
new_val = old_val - static_cast<char>(1);
|
||||
} while (_InterlockedCompareExchange8(reinterpret_cast<volatile char*>(x),
|
||||
new_val,
|
||||
old_val) != old_val);
|
||||
return static_cast<T>(new_val);
|
||||
}
|
||||
|
||||
static inline T compare_exchange(T volatile* x,
|
||||
const T new_val,
|
||||
const T expected_val) {
|
||||
return static_cast<T>(
|
||||
_InterlockedCompareExchange8(reinterpret_cast<volatile char*>(x),
|
||||
static_cast<const char>(new_val),
|
||||
static_cast<const char>(expected_val)));
|
||||
}
|
||||
|
||||
static inline T exchange(T volatile* x, const T new_val) {
|
||||
return static_cast<T>(_InterlockedExchange8(
|
||||
reinterpret_cast<volatile char*>(x), static_cast<const char>(new_val)));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct interlocked<T, 2> {
|
||||
static inline T increment(T volatile* x) {
|
||||
return static_cast<T>(
|
||||
_InterlockedIncrement16(reinterpret_cast<volatile short*>(x)));
|
||||
}
|
||||
|
||||
static inline T decrement(T volatile* x) {
|
||||
return static_cast<T>(
|
||||
_InterlockedDecrement16(reinterpret_cast<volatile short*>(x)));
|
||||
}
|
||||
|
||||
static inline T compare_exchange(T volatile* x,
|
||||
const T new_val,
|
||||
const T expected_val) {
|
||||
return static_cast<T>(
|
||||
_InterlockedCompareExchange16(reinterpret_cast<volatile short*>(x),
|
||||
static_cast<const short>(new_val),
|
||||
static_cast<const short>(expected_val)));
|
||||
}
|
||||
|
||||
static inline T exchange(T volatile* x, const T new_val) {
|
||||
return static_cast<T>(
|
||||
_InterlockedExchange16(reinterpret_cast<volatile short*>(x),
|
||||
static_cast<const short>(new_val)));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct interlocked<T, 4> {
|
||||
static inline T increment(T volatile* x) {
|
||||
return static_cast<T>(
|
||||
_InterlockedIncrement(reinterpret_cast<volatile long*>(x)));
|
||||
}
|
||||
|
||||
static inline T decrement(T volatile* x) {
|
||||
return static_cast<T>(
|
||||
_InterlockedDecrement(reinterpret_cast<volatile long*>(x)));
|
||||
}
|
||||
|
||||
static inline T compare_exchange(T volatile* x,
|
||||
const T new_val,
|
||||
const T expected_val) {
|
||||
return static_cast<T>(
|
||||
_InterlockedCompareExchange(reinterpret_cast<volatile long*>(x),
|
||||
static_cast<const long>(new_val),
|
||||
static_cast<const long>(expected_val)));
|
||||
}
|
||||
|
||||
static inline T exchange(T volatile* x, const T new_val) {
|
||||
return static_cast<T>(_InterlockedExchange(
|
||||
reinterpret_cast<volatile long*>(x), static_cast<const long>(new_val)));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct interlocked<T, 8> {
|
||||
static inline T increment(T volatile* x) {
|
||||
#if defined(_M_X64)
|
||||
return static_cast<T>(
|
||||
_InterlockedIncrement64(reinterpret_cast<volatile __int64*>(x)));
|
||||
#else
|
||||
// There's no _InterlockedIncrement64() for 32-bit x86.
|
||||
__int64 old_val, new_val;
|
||||
do {
|
||||
old_val = static_cast<__int64>(*x);
|
||||
new_val = old_val + static_cast<__int64>(1);
|
||||
} while (_InterlockedCompareExchange64(
|
||||
reinterpret_cast<volatile __int64*>(x), new_val, old_val) !=
|
||||
old_val);
|
||||
return static_cast<T>(new_val);
|
||||
#endif // _M_X64
|
||||
}
|
||||
|
||||
static inline T decrement(T volatile* x) {
|
||||
#if defined(_M_X64)
|
||||
return static_cast<T>(
|
||||
_InterlockedDecrement64(reinterpret_cast<volatile __int64*>(x)));
|
||||
#else
|
||||
// There's no _InterlockedDecrement64() for 32-bit x86.
|
||||
__int64 old_val, new_val;
|
||||
do {
|
||||
old_val = static_cast<__int64>(*x);
|
||||
new_val = old_val - static_cast<__int64>(1);
|
||||
} while (_InterlockedCompareExchange64(
|
||||
reinterpret_cast<volatile __int64*>(x), new_val, old_val) !=
|
||||
old_val);
|
||||
return static_cast<T>(new_val);
|
||||
#endif // _M_X64
|
||||
}
|
||||
|
||||
static inline T compare_exchange(T volatile* x,
|
||||
const T new_val,
|
||||
const T expected_val) {
|
||||
return static_cast<T>(_InterlockedCompareExchange64(
|
||||
reinterpret_cast<volatile __int64*>(x),
|
||||
static_cast<const __int64>(new_val),
|
||||
static_cast<const __int64>(expected_val)));
|
||||
}
|
||||
|
||||
static inline T exchange(T volatile* x, const T new_val) {
|
||||
#if defined(_M_X64)
|
||||
return static_cast<T>(
|
||||
_InterlockedExchange64(reinterpret_cast<volatile __int64*>(x),
|
||||
static_cast<const __int64>(new_val)));
|
||||
#else
|
||||
// There's no _InterlockedExchange64 for 32-bit x86.
|
||||
__int64 old_val;
|
||||
do {
|
||||
old_val = static_cast<__int64>(*x);
|
||||
} while (_InterlockedCompareExchange64(
|
||||
reinterpret_cast<volatile __int64*>(x), new_val, old_val) !=
|
||||
old_val);
|
||||
return static_cast<T>(old_val);
|
||||
#endif // _M_X64
|
||||
}
|
||||
};
|
||||
} // namespace msvc
|
||||
} // namespace atomic
|
||||
|
||||
#endif // ATOMIC_ATOMIC_MSVC_H_
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// This is free and unencumbered software released into the public domain.
|
||||
//
|
||||
// Anyone is free to copy, modify, publish, use, compile, sell, or distribute
|
||||
// this software, either in source code form or as a compiled binary, for any
|
||||
// purpose, commercial or non-commercial, and by any means.
|
||||
//
|
||||
// In jurisdictions that recognize copyright laws, the author or authors of
|
||||
// this software dedicate any and all copyright interest in the software to the
|
||||
// public domain. We make this dedication for the benefit of the public at
|
||||
// large and to the detriment of our heirs and successors. We intend this
|
||||
// dedication to be an overt act of relinquishment in perpetuity of all present
|
||||
// and future rights to this software under copyright law.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// For more information, please refer to <http://unlicense.org/>
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef ATOMIC_SPINLOCK_H_
|
||||
#define ATOMIC_SPINLOCK_H_
|
||||
|
||||
#include "atomic.h"
|
||||
|
||||
namespace atomic {
|
||||
class spinlock {
|
||||
public:
|
||||
spinlock() : value_(0) {}
|
||||
|
||||
/// @brief Acquire the lock (blocking).
|
||||
/// @note Trying to acquire a lock that is already held by the calling thread
|
||||
/// will dead-lock (block indefinitely).
|
||||
void lock() {
|
||||
while (!value_.compare_exchange(UNLOCKED, LOCKED))
|
||||
;
|
||||
}
|
||||
|
||||
/// @brief Release the lock.
|
||||
/// @note It is an error to release a lock that has not been previously
|
||||
/// acquired.
|
||||
void unlock() { value_.store(UNLOCKED); }
|
||||
|
||||
private:
|
||||
static const int UNLOCKED = 0;
|
||||
static const int LOCKED = 1;
|
||||
|
||||
atomic<int> value_;
|
||||
|
||||
ATOMIC_DISALLOW_COPY(spinlock)
|
||||
};
|
||||
|
||||
class lock_guard {
|
||||
public:
|
||||
/// @brief The constructor acquires the lock.
|
||||
/// @param lock The spinlock that will be locked.
|
||||
explicit lock_guard(spinlock& lock) : lock_(lock) {
|
||||
lock_.lock();
|
||||
}
|
||||
|
||||
/// @brief The destructor releases the lock.
|
||||
~lock_guard() {
|
||||
lock_.unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
spinlock& lock_;
|
||||
|
||||
ATOMIC_DISALLOW_COPY(lock_guard)
|
||||
};
|
||||
|
||||
} // namespace atomic
|
||||
|
||||
#endif // ATOMIC_SPINLOCK_H_
|
||||
|
|
@ -217,7 +217,7 @@ public:
|
|||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
Registrar <X> ()
|
||||
Registrar ()
|
||||
: mp_first (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
|
|
|
|||
|
|
@ -175,13 +175,13 @@ public:
|
|||
typedef typename receivers::iterator receivers_iterator;
|
||||
#endif
|
||||
|
||||
event<_TMPLARGLISTP> ()
|
||||
event ()
|
||||
: mp_destroyed_sentinel (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
~event<_TMPLARGLISTP> ()
|
||||
~event ()
|
||||
{
|
||||
if (mp_destroyed_sentinel) {
|
||||
*mp_destroyed_sentinel = true;
|
||||
|
|
|
|||
|
|
@ -412,9 +412,9 @@ JobBase::stop ()
|
|||
m_stopping = false;
|
||||
m_running = false;
|
||||
|
||||
m_lock.unlock ();
|
||||
|
||||
stopped ();
|
||||
|
||||
m_lock.unlock ();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -20,15 +20,17 @@
|
|||
|
||||
*/
|
||||
|
||||
#if !defined(HAVE_QT) || defined(HAVE_PTHREADS)
|
||||
|
||||
#include "tlThreads.h"
|
||||
#include "tlUtils.h"
|
||||
#include "tlTimer.h"
|
||||
#include "tlSleep.h"
|
||||
#include "tlLog.h"
|
||||
#include "tlInternational.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
#if !defined(HAVE_QT) || defined(HAVE_PTHREADS)
|
||||
|
||||
#define _TIMESPEC_DEFINED // avoids errors with pthread-win and MSVC2017
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
|
|
@ -39,12 +41,71 @@
|
|||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
namespace tl
|
||||
{
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
// WaitCondition implementation
|
||||
|
||||
#if defined(HAVE_CPP20) || !defined(HAVE_QT) || defined(HAVE_PTHREADS)
|
||||
|
||||
#if defined(HAVE_CPP20)
|
||||
|
||||
class WaitConditionPrivate
|
||||
{
|
||||
public:
|
||||
WaitConditionPrivate ()
|
||||
: m_condition ()
|
||||
{
|
||||
}
|
||||
|
||||
~WaitConditionPrivate ()
|
||||
{
|
||||
}
|
||||
|
||||
bool wait (Mutex *mutex, unsigned long time)
|
||||
{
|
||||
bool result = true;
|
||||
m_condition.clear (std::memory_order_release);
|
||||
|
||||
mutex->unlock ();
|
||||
|
||||
if (time != std::numeric_limits<unsigned long>::max ()) {
|
||||
while (time > 0 && ! m_condition.test (std::memory_order_acquire)) {
|
||||
tl::usleep (1000);
|
||||
--time;
|
||||
}
|
||||
result = (time != 0);
|
||||
} else {
|
||||
m_condition.wait (false);
|
||||
}
|
||||
|
||||
mutex->lock ();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void wake_all ()
|
||||
{
|
||||
if (! m_condition.test_and_set (std::memory_order_acquire)) {
|
||||
m_condition.notify_all ();
|
||||
}
|
||||
}
|
||||
|
||||
void wake_one ()
|
||||
{
|
||||
if (! m_condition.test_and_set (std::memory_order_acquire)) {
|
||||
m_condition.notify_one ();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic_flag m_condition;
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
class WaitConditionPrivate
|
||||
{
|
||||
|
|
@ -139,6 +200,8 @@ private:
|
|||
bool m_initialized;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
WaitCondition::WaitCondition ()
|
||||
{
|
||||
mp_data = new WaitConditionPrivate ();
|
||||
|
|
@ -165,9 +228,13 @@ void WaitCondition::wakeOne ()
|
|||
mp_data->wake_one ();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
// Thread implementation
|
||||
|
||||
#if !defined(HAVE_QT) || defined(HAVE_PTHREADS)
|
||||
|
||||
class ThreadPrivateData
|
||||
{
|
||||
public:
|
||||
|
|
@ -401,6 +468,6 @@ ThreadStorageHolderBase *ThreadStorageBase::holder ()
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,8 +33,7 @@
|
|||
# include <QThread>
|
||||
# include <QThreadStorage>
|
||||
#else
|
||||
// atomics taken from https://github.com/mbitsnbites/atomic
|
||||
# include "atomic/spinlock.h"
|
||||
# include <atomic>
|
||||
#endif
|
||||
|
||||
namespace tl
|
||||
|
|
@ -60,11 +59,21 @@ public:
|
|||
class TL_PUBLIC Mutex
|
||||
{
|
||||
public:
|
||||
Mutex () : m_spinlock () { }
|
||||
void lock () { m_spinlock.lock (); }
|
||||
void unlock () { m_spinlock.unlock (); }
|
||||
Mutex () { }
|
||||
|
||||
void lock ()
|
||||
{
|
||||
while (flag.test_and_set (std::memory_order_acquire))
|
||||
;
|
||||
}
|
||||
|
||||
void unlock ()
|
||||
{
|
||||
flag.clear (std::memory_order_release);
|
||||
}
|
||||
|
||||
private:
|
||||
atomic::spinlock m_spinlock;
|
||||
std::atomic_flag flag = ATOMIC_FLAG_INIT;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -75,7 +84,7 @@ private:
|
|||
* available.
|
||||
*/
|
||||
|
||||
#if defined(HAVE_QT) && !defined(HAVE_PTHREADS)
|
||||
#if defined(HAVE_QT) && !defined(HAVE_PTHREADS) && !defined(HAVE_CPP20)
|
||||
|
||||
class TL_PUBLIC WaitCondition
|
||||
: public QWaitCondition
|
||||
|
|
|
|||
|
|
@ -61,6 +61,26 @@ class DBShapesTest(unittest.TestCase):
|
|||
shapes = top.shapes(l1)
|
||||
self.assertEqual(sys.getrefcount(shapes), 2)
|
||||
|
||||
# Tests the ability to take PolygonWithProperties instead of base class
|
||||
# for setter
|
||||
def test_3(self):
|
||||
|
||||
shapes = pya.Shapes()
|
||||
shapes.insert(pya.Polygon(pya.Box(0, 0, 100, 200)))
|
||||
self.assertEqual(";".join([ str(s) for s in shapes.each() ]), "polygon (0,0;0,200;100,200;100,0)")
|
||||
|
||||
pwp = pya.PolygonWithProperties(pya.Polygon(pya.Box(0, 0, 110, 210)))
|
||||
s0 = [ s for s in shapes.each() ][0]
|
||||
s0.polygon = pwp
|
||||
self.assertEqual(";".join([ str(s) for s in shapes.each() ]), "polygon (0,0;0,210;110,210;110,0)")
|
||||
|
||||
# with object substitution
|
||||
s0.polygon = ( pya.Box(0, 0, 120, 220) )
|
||||
self.assertEqual(";".join([ str(s) for s in shapes.each() ]), "polygon (0,0;0,220;120,220;120,0)")
|
||||
|
||||
# with object substitution by constructor inference
|
||||
s0.polygon = pya.Box(0, 0, 130, 230)
|
||||
self.assertEqual(";".join([ str(s) for s in shapes.each() ]), "polygon (0,0;0,230;130,230;130,0)")
|
||||
|
||||
# run unit tests
|
||||
if __name__ == '__main__':
|
||||
|
|
|
|||
|
|
@ -1205,6 +1205,77 @@ class RDB_TestClass < TestBase
|
|||
|
||||
end
|
||||
|
||||
# apply
|
||||
def test_16
|
||||
|
||||
rdb1 = RBA::ReportDatabase::new
|
||||
cat = rdb1.create_category("CAT")
|
||||
cell = rdb1.create_cell("TOP")
|
||||
item = rdb1.create_item(cell, cat)
|
||||
item.add_value("item1")
|
||||
item.add_tag(rdb1.tag_id("t1"))
|
||||
|
||||
assert_equal(item.tags_str, "t1")
|
||||
|
||||
rdb2 = RBA::ReportDatabase::new
|
||||
cat = rdb2.create_category("CAT")
|
||||
cell = rdb2.create_cell("TOP")
|
||||
item = rdb2.create_item(cell, cat)
|
||||
item.add_value("item1")
|
||||
item.add_tag(rdb2.tag_id("t2"))
|
||||
|
||||
assert_equal(item.tags_str, "t2")
|
||||
|
||||
items = rdb1.each_item.to_a
|
||||
assert_equal(items.size, 1)
|
||||
|
||||
assert_equal(items[0].tags_str, "t1")
|
||||
|
||||
rdb1.apply(rdb2)
|
||||
|
||||
items = rdb1.each_item.to_a
|
||||
assert_equal(items.size, 1)
|
||||
|
||||
assert_equal(items[0].tags_str, "t2")
|
||||
|
||||
end
|
||||
|
||||
# merge
|
||||
def test_17
|
||||
|
||||
rdb1 = RBA::ReportDatabase::new
|
||||
cat = rdb1.create_category("CAT")
|
||||
cell = rdb1.create_cell("TOP")
|
||||
item = rdb1.create_item(cell, cat)
|
||||
item.add_value("item1")
|
||||
item.add_tag(rdb1.tag_id("t1"))
|
||||
|
||||
assert_equal(item.tags_str, "t1")
|
||||
|
||||
rdb2 = RBA::ReportDatabase::new
|
||||
cat = rdb2.create_category("CAT")
|
||||
cell = rdb2.create_cell("TOP")
|
||||
item = rdb2.create_item(cell, cat)
|
||||
item.add_value("item1")
|
||||
item.add_tag(rdb2.tag_id("t2"))
|
||||
|
||||
assert_equal(item.tags_str, "t2")
|
||||
|
||||
items = rdb1.each_item.to_a
|
||||
assert_equal(items.size, 1)
|
||||
|
||||
assert_equal(items[0].tags_str, "t1")
|
||||
|
||||
rdb1.merge(rdb2)
|
||||
|
||||
items = rdb1.each_item.to_a
|
||||
assert_equal(items.size, 2)
|
||||
|
||||
assert_equal(items[0].tags_str, "t1")
|
||||
assert_equal(items[1].tags_str, "t2")
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
load("test_epilogue.rb")
|
||||
|
|
|
|||
Loading…
Reference in New Issue