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

This commit is contained in:
Matthias Koefferlein 2024-07-30 18:59:18 +02:00
commit 1040e81fe6
74 changed files with 2249 additions and 604 deletions

View File

@ -63,16 +63,16 @@ static std::vector<ant::Template> make_standard_templates ()
templates.push_back (ant::Template (tl::to_string (tr ("Measure edge")), "$X", "$Y", "$D", ant::Object::STY_ruler, ant::Object::OL_diag, true, lay::AC_Global, "_measure_edge"));
templates.back ().set_mode (ant::Template::RulerAutoMetricEdge);
templates.push_back (ant::Template (tl::to_string (tr ("Angle")), "", "", "$(sprintf('%.5g',G))°", ant::Object::STY_line, ant::Object::OL_angle, true, lay::AC_Global, "_angle"));
templates.push_back (ant::Template (tl::to_string (tr ("Angle")), "", "", "$(sprintf('%.5g',G))°", ant::Object::STY_line, ant::Object::OL_angle, true, lay::AC_Any, "_angle"));
templates.back ().set_mode (ant::Template::RulerThreeClicks);
templates.push_back (ant::Template (tl::to_string (tr ("Radius")), "", "", "R=$D", ant::Object::STY_arrow_end, ant::Object::OL_radius, true, lay::AC_Global, "_radius"));
templates.push_back (ant::Template (tl::to_string (tr ("Radius")), "", "", "R=$D", ant::Object::STY_arrow_end, ant::Object::OL_radius, true, lay::AC_Any, "_radius"));
templates.back ().set_mode (ant::Template::RulerThreeClicks);
templates.back ().set_main_position (ant::Object::POS_center);
templates.push_back (ant::Template (tl::to_string (tr ("Ellipse")), "W=$(abs(X))", "H=$(abs(Y))", "", ant::Object::STY_line, ant::Object::OL_ellipse, true, lay::AC_Global, std::string ()));
templates.push_back (ant::Template (tl::to_string (tr ("Ellipse")), "W=$(abs(X))", "H=$(abs(Y))", "", ant::Object::STY_line, ant::Object::OL_ellipse, true, lay::AC_Any, std::string ()));
templates.push_back (ant::Template (tl::to_string (tr ("Box")), "W=$(abs(X))", "H=$(abs(Y))", "", ant::Object::STY_line, ant::Object::OL_box, true, lay::AC_Global, std::string ()));
templates.push_back (ant::Template (tl::to_string (tr ("Box")), "W=$(abs(X))", "H=$(abs(Y))", "", ant::Object::STY_line, ant::Object::OL_box, true, lay::AC_Any, std::string ()));
return templates;
}

View File

@ -272,6 +272,11 @@ module RBA
# of a PCell
class PCellDeclarationHelper &lt; PCellDeclaration
# makes PCellDeclaration's "layout" method available
if ! self.method_defined?(:_layout_base)
alias_method :_layout_base, :layout
end
# import the Type... constants from PCellParameterDeclaration
PCellParameterDeclaration.constants.each do |c|
if !const_defined?(c)
@ -290,18 +295,58 @@ module RBA
@cell = nil
@layer_param_index = []
@layers = nil
@state_stack = []
end
# provide accessors for the current layout and cell (for prod)
attr_reader :layout, :cell, :shape, :layer
attr_reader :cell, :shape, :layer
def layout
@layout || _layout_base()
end
# provide fallback accessors in case of a name clash with a
# parameter
def _layer; @layer; end
def _layout; @layout; end
def _layout; @layout || _layout_base(); end
def _cell; @cell; end
def _shape; @shape; end
# Starts an operation - pushes the state on the state stack
def start
@state_stack &lt;&lt; [ @param_values, @param_states, @layers, @cell, @layout, @layer, @shape ]
self._reset_state
end
# Finishes an operation - pops the state from the state stack
def finish
if ! @state_stack.empty?
@param_values, @param_states, @layers, @cell, @layout, @layer, @shape = @state_stack.pop
else
self._reset_state
end
end
# Resets the state to default values
def _reset_state
@param_values = nil
@param_states = nil
@layers = nil
@layout = nil
# This should be here:
# @cell = nil
# @layer = nil
# @shape = nil
# but this would break backward compatibility of "display_text" (actually
# exploiting this bug) - fix this in the next major release.
end
# A helper method to access the nth parameter
def _get_param(nth, name)
@ -410,11 +455,13 @@ module RBA
# implementation of display_text
def display_text(parameters)
self.start
@param_values = parameters
text = ""
begin
text = display_text_impl
ensure
@param_values = nil
self.finish
end
text
end
@ -431,34 +478,33 @@ module RBA
# coerce parameters (make consistent)
def coerce_parameters(layout, parameters)
self.start
@param_values = parameters
@layout = layout
ret = parameters
begin
coerce_parameters_impl
ensure
@layout = nil
ret = @param_values
@param_values = nil
self.finish
end
ret
end
# parameter change callback
def callback(layout, name, states)
@param_values = nil
self.start
@param_states = states
@layout = layout
begin
callback_impl(name)
ensure
@param_states = nil
@layout = nil
self.finish
end
end
# produce the layout
def produce(layout, layers, parameters, cell)
self.start
@layers = layers
@cell = cell
@param_values = parameters
@ -466,15 +512,13 @@ module RBA
begin
produce_impl
ensure
@layers = nil
@cell = nil
@param_values = nil
@layout = nil
self.finish
end
end
# produce a helper for can_create_from_shape
def can_create_from_shape(layout, shape, layer)
self.start
ret = false
@layout = layout
@shape = shape
@ -482,24 +526,22 @@ module RBA
begin
ret = can_create_from_shape_impl
ensure
@layout = nil
@shape = nil
@layer = nil
self.finish
end
ret
end
# produce a helper for parameters_from_shape
# produce a helper for transformation_from_shape
def transformation_from_shape(layout, shape, layer)
self.start
@layout = layout
@shape = shape
@layer = layer
t = nil
begin
t = transformation_from_shape_impl
ensure
@layout = nil
@shape = nil
@layer = nil
self.finish
end
t
end
@ -507,6 +549,7 @@ module RBA
# produce a helper for parameters_from_shape
# with this helper, the implementation can use the parameter setters
def parameters_from_shape(layout, shape, layer)
self.start
@param_values = @param_decls.map { |pd| pd.default }
@layout = layout
@shape = shape
@ -514,11 +557,10 @@ module RBA
begin
parameters_from_shape_impl
ensure
@layout = nil
@shape = nil
@layer = nil
ret = @param_values
self.finish
end
@param_values
ret
end
# default implementation

View File

@ -148,7 +148,11 @@ struct DB_PUBLIC InstElement
*/
db::ICplxTrans complex_trans () const
{
return inst_ptr.cell_inst ().complex_trans (*array_inst);
if (array_inst.at_end ()) {
return inst_ptr.cell_inst ().complex_trans ();
} else {
return inst_ptr.cell_inst ().complex_trans (*array_inst);
}
}
/**

View File

@ -2887,15 +2887,18 @@ Layout::get_context_info (cell_index_type cell_index, LayoutOrCellContextInfo &i
if (pcell_variant) {
const db::PCellDeclaration *pcell_decl = ly->pcell_declaration (pcell_variant->pcell_id ());
const std::vector<db::PCellParameterDeclaration> &pcp = pcell_decl->parameter_declarations ();
std::vector<db::PCellParameterDeclaration>::const_iterator pd = pcp.begin ();
for (std::vector<tl::Variant>::const_iterator p = pcell_variant->parameters ().begin (); p != pcell_variant->parameters ().end () && pd != pcp.end (); ++p, ++pd) {
info.pcell_parameters.insert (std::make_pair (pd->get_name (), *p));
if (pcell_decl) {
const std::vector<db::PCellParameterDeclaration> &pcp = pcell_decl->parameter_declarations ();
std::vector<db::PCellParameterDeclaration>::const_iterator pd = pcp.begin ();
for (std::vector<tl::Variant>::const_iterator p = pcell_variant->parameters ().begin (); p != pcell_variant->parameters ().end () && pd != pcp.end (); ++p, ++pd) {
info.pcell_parameters.insert (std::make_pair (pd->get_name (), *p));
}
}
const db::PCellHeader *header = ly->pcell_header (pcell_variant->pcell_id ());
info.pcell_name = header->get_name ();
if (header) {
info.pcell_name = header->get_name ();
}
} else if (ly != this) {
info.cell_name = ly->cell_name (cptr->cell_index ());

View File

@ -683,5 +683,106 @@ scale_and_snap (db::Layout &layout, db::Cell &cell, db::Coord g, db::Coord m, db
}
}
// ------------------------------------------------------------
// break_polygons implementation
static bool split_polygon (bool first, db::Polygon &poly, size_t max_vertex_count, double max_area_ratio, std::vector<db::Polygon> &parts)
{
if ((max_vertex_count > 0 && poly.vertices () > max_vertex_count) ||
(max_area_ratio > 0 && poly.area_ratio () > max_area_ratio)) {
std::vector<db::Polygon> sp;
db::split_polygon (poly, sp);
for (auto p = sp.begin (); p != sp.end (); ++p) {
split_polygon (false, *p, max_vertex_count, max_area_ratio, parts);
}
return true;
} else {
if (! first) {
parts.push_back (db::Polygon ());
parts.back ().swap (poly);
}
return false;
}
}
void
break_polygons (db::Shapes &shapes, size_t max_vertex_count, double max_area_ratio)
{
if (shapes.is_editable ()) {
std::vector<db::Polygon> new_polygons;
std::vector<db::Shape> to_delete;
for (auto s = shapes.begin (db::ShapeIterator::Polygons | db::ShapeIterator::Paths); ! s.at_end (); ++s) {
db::Polygon poly;
s->instantiate (poly);
if (split_polygon (true, poly, max_vertex_count, max_area_ratio, new_polygons)) {
to_delete.push_back (*s);
}
}
shapes.erase_shapes (to_delete);
for (auto p = new_polygons.begin (); p != new_polygons.end (); ++p) {
shapes.insert (*p);
}
} else {
// In non-editable mode we cannot do "erase", so we use a temporary, editable Shapes container
db::Shapes tmp (true);
tmp.insert (shapes);
shapes.clear ();
break_polygons (tmp, max_vertex_count, max_area_ratio);
shapes.insert (tmp);
tl_assert (!shapes.is_editable ());
}
}
void
break_polygons (db::Layout &layout, db::cell_index_type cell_index, unsigned int layer, size_t max_vertex_count, double max_area_ratio)
{
if (layout.is_valid_cell_index (cell_index) && layout.is_valid_layer (layer)) {
db::Cell &cell = layout.cell (cell_index);
break_polygons (cell.shapes (layer), max_vertex_count, max_area_ratio);
}
}
void
break_polygons (db::Layout &layout, unsigned int layer, size_t max_vertex_count, double max_area_ratio)
{
for (db::cell_index_type ci = 0; ci < layout.cells (); ++ci) {
if (layout.is_valid_cell_index (ci)) {
db::Cell &cell = layout.cell (ci);
break_polygons (cell.shapes (layer), max_vertex_count, max_area_ratio);
}
}
}
void
break_polygons (db::Layout &layout, size_t max_vertex_count, double max_area_ratio)
{
for (db::cell_index_type ci = 0; ci < layout.cells (); ++ci) {
if (layout.is_valid_cell_index (ci)) {
db::Cell &cell = layout.cell (ci);
for (unsigned int li = 0; li < layout.layers (); ++li) {
if (layout.is_valid_layer (li)) {
break_polygons (cell.shapes (li), max_vertex_count, max_area_ratio);
}
}
}
}
}
}

View File

@ -249,6 +249,33 @@ private:
*/
DB_PUBLIC void scale_and_snap (db::Layout &layout, db::Cell &cell, db::Coord g, db::Coord m, db::Coord d);
/**
* @brief Breaks polygons according to max_vertex_count and max_area_ratio
*
* This method will investigate all polygons on the given layer and cell and split them in case they
* have more than the specified vertices and an bounding-box area to polygon area ratio larget
* than the specified max_area_ratio. This serves optimization for algorithms needing a good
* bounding box approximation.
*
* Setting max_vertex_count or max_area_ratio to 0 disables the respective check.
*/
DB_PUBLIC void break_polygons (db::Layout &layout, db::cell_index_type cell_index, unsigned int layer, size_t max_vertex_count, double max_area_ratio);
/**
* @brief Like "break_polygons" before, but applies it to all cells.
*/
DB_PUBLIC void break_polygons (db::Layout &layout, unsigned int layer, size_t max_vertex_count, double max_area_ratio);
/**
* @brief Like "break_polygons" before, but applies it to all cells and all layers.
*/
DB_PUBLIC void break_polygons (db::Layout &layout, size_t max_vertex_count, double max_area_ratio);
/**
* @brief Like "break_polygons" before, but applies it to the given Shapes container.
*/
DB_PUBLIC void break_polygons (db::Shapes &shapes, size_t max_vertex_count, double max_area_ratio);
} // namespace db
#endif

View File

@ -366,6 +366,47 @@ size_t Netlist::top_circuit_count () const
return m_top_circuits;
}
Circuit *Netlist::top_circuit ()
{
size_t ntop = top_circuit_count ();
if (ntop == 0) {
return 0;
} else if (ntop > 1) {
throw tl::Exception (tl::to_string (tr ("Netlist contains more than a single top circuit")));
} else {
return begin_top_down ().operator-> ();
}
}
const Circuit *Netlist::top_circuit () const
{
return const_cast<Netlist *> (this)->top_circuit ();
}
std::vector<Circuit *> Netlist::top_circuits ()
{
size_t ntop = top_circuit_count ();
std::vector<Circuit *> result;
result.reserve (ntop);
for (auto c = begin_top_down (); ntop > 0 && c != end_top_down (); ++c) {
result.push_back (c.operator-> ());
--ntop;
}
return result;
}
std::vector<const Circuit *> Netlist::top_circuits () const
{
size_t ntop = top_circuit_count ();
std::vector<const Circuit *> result;
result.reserve (ntop);
for (auto c = begin_top_down (); ntop > 0 && c != end_top_down (); ++c) {
result.push_back (c.operator-> ());
--ntop;
}
return result;
}
Netlist::bottom_up_circuit_iterator Netlist::begin_bottom_up ()
{
if (! m_valid_topology) {

View File

@ -271,6 +271,32 @@ public:
return m_circuit_by_cell_index.object_by (cell_index);
}
/**
* @brief Gets the top circuit if there is one
* This method will assert if there is more than a single top circuit.
* It will return 0 if there is no top circuit.
*/
Circuit *top_circuit ();
/**
* @brief Gets the top circuit if there is one (const version)
* This method will assert if there is more than a single top circuit.
* It will return 0 if there is no top circuit.
*/
const Circuit *top_circuit () const;
/**
* @brief Gets the top circuits
* This convenience method will return a list of top circuits.
*/
std::vector<Circuit *> top_circuits ();
/**
* @brief Gets the top circuits (const version)
* This convenience method will return a list of top circuits.
*/
std::vector<const Circuit *> top_circuits () const;
/**
* @brief Gets the top-down circuits iterator (begin)
* This iterator will deliver the circuits in a top-down way - i.e. child circuits

View File

@ -54,7 +54,7 @@ read_param_card (tl::Extractor &ex, const db::Netlist *netlist, std::map<std::st
// taken from:
// https://nmg.gitlab.io/ngspice-manual/circuitdescription/paramparametricnetlists/paramline.html
while (! ex.at_end ()) {
while (! NetlistSpiceReader::at_eol (ex)) {
std::string name;
ex.read_word (name);
@ -333,7 +333,7 @@ read_name (tl::Extractor &ex, const db::Netlist *netlist)
{
std::string n;
ex.read_word_or_quoted (n, allowed_name_chars);
return netlist->normalize_name (n);
return netlist->normalize_name (NetlistSpiceReader::unescape_name (n));
}
class SpiceCircuitDict
@ -610,7 +610,7 @@ SpiceCircuitDict::get_line ()
std::string path_or_libname;
ex.read_word_or_quoted (path_or_libname, allowed_name_chars);
if (! ex.at_end ()) {
if (! NetlistSpiceReader::at_eol (ex)) {
std::string libname;
ex.read_word_or_quoted (libname, allowed_name_chars);
@ -648,7 +648,7 @@ SpiceCircuitDict::get_line ()
ex.expect_end ();
} else if (ex.at_end () || ex.test ("*")) {
} else if (NetlistSpiceReader::at_eol (ex)) {
// skip empty and comment lines
@ -680,7 +680,7 @@ SpiceCircuitDict::read_card ()
} else if (ex.test_without_case (".global")) {
while (! ex.at_end ()) {
while (! NetlistSpiceReader::at_eol (ex)) {
std::string n = mp_delegate->translate_net_name (read_name (ex, mp_netlist));
if (m_global_net_names.find (n) == m_global_net_names.end ()) {
m_global_nets.push_back (n);
@ -766,7 +766,7 @@ SpiceCircuitDict::read_card ()
void
SpiceCircuitDict::read_options (tl::Extractor &ex)
{
while (! ex.at_end ()) {
while (! NetlistSpiceReader::at_eol (ex)) {
std::string n;
ex.read_word_or_quoted (n, allowed_name_chars);
@ -780,7 +780,7 @@ SpiceCircuitDict::read_options (tl::Extractor &ex)
} else {
// skip until end or next space
ex.skip ();
while (! ex.at_end () && ! isspace (*ex)) {
while (! NetlistSpiceReader::at_eol (ex) && ! isspace (*ex)) {
++ex;
}
}
@ -1345,4 +1345,106 @@ void NetlistSpiceReader::read (tl::InputStream &stream, db::Netlist &netlist)
}
}
bool NetlistSpiceReader::at_eol (tl::Extractor &ex)
{
// terminate at end or midline comment
return ex.at_end () || ex.test ("*") || ex.test (";");
}
inline static int hex_num (char c)
{
if (c >= '0' && c <= '9') {
return (int (c - '0'));
} else if (c >= 'a' && c <= 'f') {
return (int (c - 'f') + 10);
} else {
return -1;
}
}
std::string NetlistSpiceReader::unescape_name (const std::string &n)
{
std::string nn;
nn.reserve (n.size ());
char quote = 0;
const char *cp = n.c_str ();
while (*cp) {
if (*cp == quote) {
quote = 0;
++cp;
} else if (! quote && (*cp == '"' || *cp == '\'')) {
quote = *cp++;
} else if (*cp == '\\' && cp[1]) {
if (tolower (cp[1]) == 'x') {
cp += 2;
char c = 0;
for (int i = 0; i < 2 && *cp; ++i) {
int n = hex_num (*cp);
if (n >= 0) {
++cp;
c = c * 16 + char (n);
} else {
break;
}
}
nn += c;
} else {
++cp;
nn += *cp++;
}
} else {
nn += *cp++;
}
}
return nn;
}
std::string NetlistSpiceReader::parse_component (tl::Extractor &ex)
{
const char *cp = ex.skip ();
const char *cp0 = cp;
char quote = 0;
unsigned int brackets = 0;
while (*cp) {
if (quote) {
if (*cp == quote) {
quote = 0;
} else if (*cp == '\\' && cp[1]) {
++cp;
}
} else if ((isspace (*cp) || *cp == '=') && ! brackets) {
break;
} else if (*cp == '"' || *cp == '\'') {
quote = *cp;
} else if (*cp == '(') {
++brackets;
} else if (*cp == ')') {
if (brackets > 0) {
--brackets;
}
}
++cp;
}
ex = tl::Extractor (cp);
return std::string (cp0, cp - cp0);
}
}

View File

@ -29,6 +29,7 @@
#include "tlStream.h"
#include "tlObject.h"
#include "tlVariant.h"
#include "tlString.h"
#include <map>
#include <string>
@ -63,6 +64,29 @@ public:
m_strict = s;
}
/**
* @brief Returns true, if the extractor is at the end of the line
* "at_eol" is true at the line end or when a midline comment starts.
*/
static bool at_eol (tl::Extractor &ex);
/**
* @brief Unescapes a name
* Replaces backslash sequences with the true character and removes quotes.
*/
static std::string unescape_name (const std::string &n);
/**
* @brief Parses a netlist component (net name, expression etc.)
* Scans over the expression or net name and returns a string representing the latter.
*/
static std::string parse_component (tl::Extractor &ex);
/**
* @brief Reads a component name
* Scans over a component name and returns the
*/
private:
tl::weak_ptr<NetlistSpiceReaderDelegate> mp_delegate;
std::unique_ptr<NetlistSpiceReaderDelegate> mp_default_delegate;

View File

@ -32,60 +32,6 @@ namespace db
// ------------------------------------------------------------------------------------------------------
inline static int hex_num (char c)
{
if (c >= '0' && c <= '9') {
return (int (c - '0'));
} else if (c >= 'a' && c <= 'f') {
return (int (c - 'f') + 10);
} else {
return -1;
}
}
static std::string unescape_name (const std::string &n)
{
std::string nn;
nn.reserve (n.size ());
const char *cp = n.c_str ();
while (*cp) {
if (*cp == '\\' && cp[1]) {
if (tolower (cp[1]) == 'x') {
cp += 2;
char c = 0;
for (int i = 0; i < 2 && *cp; ++i) {
int n = hex_num (*cp);
if (n >= 0) {
++cp;
c = c * 16 + char (n);
} else {
break;
}
}
nn += c;
} else {
++cp;
nn += *cp++;
}
} else {
nn += *cp++;
}
}
return nn;
}
// ------------------------------------------------------------------------------------------------------
NetlistSpiceReaderOptions::NetlistSpiceReaderOptions ()
{
scale = 1.0;
@ -147,7 +93,7 @@ bool NetlistSpiceReaderDelegate::wants_subcircuit (const std::string & /*circuit
std::string NetlistSpiceReaderDelegate::translate_net_name (const std::string &nn)
{
return unescape_name (nn);
return NetlistSpiceReader::unescape_name (nn);
}
void NetlistSpiceReaderDelegate::error (const std::string &msg)
@ -172,45 +118,12 @@ static db::DeviceClass *make_device_class (db::Circuit *circuit, const std::stri
return cls;
}
static std::string parse_component (tl::Extractor &ex)
{
const char *cp = ex.skip ();
const char *cp0 = cp;
char quote = 0;
unsigned int brackets = 0;
while (*cp) {
if (quote) {
if (*cp == quote) {
quote = 0;
} else if (*cp == '\\' && cp[1]) {
++cp;
}
} else if ((isspace (*cp) || *cp == '=') && ! brackets) {
break;
} else if (*cp == '"' || *cp == '\'') {
quote = *cp;
} else if (*cp == '(') {
++brackets;
} else if (*cp == ')') {
if (brackets > 0) {
--brackets;
}
}
++cp;
}
ex = tl::Extractor (cp);
return std::string (cp0, cp - cp0);
}
void NetlistSpiceReaderDelegate::parse_element_components (const std::string &s, std::vector<std::string> &strings, std::map<std::string, tl::Variant> &pv, const std::map<std::string, tl::Variant> &variables)
{
tl::Extractor ex (s.c_str ());
bool in_params = false;
while (! ex.at_end ()) {
while (! NetlistSpiceReader::at_eol (ex)) {
if (ex.test_without_case ("params:")) {
@ -235,7 +148,7 @@ void NetlistSpiceReaderDelegate::parse_element_components (const std::string &s,
ex.error (tl::to_string (tr ("Invalid syntax for parameter assignment - needs keyword followed by '='")));
}
std::string comp_name = parse_component (ex);
std::string comp_name = NetlistSpiceReader::parse_component (ex);
comp_name = mp_netlist ? mp_netlist->normalize_name (comp_name) : tl::to_upper_case (comp_name);
// resolve variables if string type

View File

@ -32,6 +32,7 @@ namespace db
// Recursive shape iterator implementation
RecursiveShapeIterator::RecursiveShapeIterator (const RecursiveShapeIterator &d)
: gsi::ObjectBase (d)
{
operator= (d);
}

View File

@ -1104,14 +1104,14 @@ static void set_cell_property (db::Cell *c, const tl::Variant &key, const tl::Va
c->prop_id (layout->properties_repository ().properties_id (props));
}
static tl::Variant get_cell_property (db::Cell *c, const tl::Variant &key)
static tl::Variant get_cell_property (const db::Cell *c, const tl::Variant &key)
{
db::properties_id_type id = c->prop_id ();
if (id == 0) {
return tl::Variant ();
}
db::Layout *layout = c->layout ();
const db::Layout *layout = c->layout ();
if (! layout) {
throw tl::Exception (tl::to_string (tr ("Cell does not reside inside a layout - cannot retrieve properties")));
}
@ -1130,6 +1130,26 @@ static tl::Variant get_cell_property (db::Cell *c, const tl::Variant &key)
}
}
static tl::Variant get_cell_properties (const db::Cell *c)
{
db::properties_id_type id = c->prop_id ();
if (id == 0) {
return tl::Variant::empty_array ();
}
const db::Layout *layout = c->layout ();
if (! layout) {
throw tl::Exception (tl::to_string (tr ("Cell does not reside inside a layout - cannot retrieve properties")));
}
tl::Variant res = tl::Variant::empty_array ();
const db::PropertiesRepository::properties_set &props = layout->properties_repository ().properties (id);
for (auto i = props.begin (); i != props.end (); ++i) {
res.insert (layout->properties_repository ().prop_name (i->first), i->second);
}
return res;
}
static bool is_pcell_variant (const db::Cell *cell)
{
tl_assert (cell->layout () != 0);
@ -1841,10 +1861,16 @@ Class<db::Cell> decl_Cell ("db", "Cell",
"@brief Gets the user property with the given key\n"
"This method is a convenience method that gets the property with the given key. "
"If no property with that key exists, it will return nil. Using that method is more "
"convenient than using the layout object and the properties ID to retrieve the property value. "
"convenient than using the layout object and the properties ID to retrieve the property value.\n"
"\n"
"This method has been introduced in version 0.23."
) +
gsi::method_ext ("properties", &get_cell_properties,
"@brief Gets the user properties as a hash\n"
"This method is a convenience method that gets all user properties as a single hash.\n"
"\n"
"This method has been introduced in version 0.29.5."
) +
gsi::method_ext ("add_meta_info", &cell_add_meta_info, gsi::arg ("info"),
"@brief Adds meta information to the cell\n"
"See \\LayoutMetaInfo for details about cells and meta information.\n"
@ -3539,6 +3565,26 @@ static tl::Variant get_property (const db::Instance *i, const tl::Variant &key)
}
}
static tl::Variant get_properties (const db::Instance *i)
{
db::properties_id_type id = i->prop_id ();
if (id == 0) {
return tl::Variant::empty_array ();
}
const db::Layout *layout = layout_ptr_const (i);
if (! layout) {
throw tl::Exception (tl::to_string (tr ("Instance does not reside inside a layout - cannot retrieve properties")));
}
tl::Variant res = tl::Variant::empty_array ();
const db::PropertiesRepository::properties_set &props = layout->properties_repository ().properties (id);
for (auto i = props.begin (); i != props.end (); ++i) {
res.insert (layout->properties_repository ().prop_name (i->first), i->second);
}
return res;
}
static bool inst_is_valid (const db::Instance *inst)
{
return inst->instances () && inst->instances ()->is_valid (*inst);
@ -4011,6 +4057,12 @@ Class<db::Instance> decl_Instance ("db", "Instance",
"\n"
"This method has been introduced in version 0.22."
) +
gsi::method_ext ("properties", &get_properties,
"@brief Gets the user properties as a hash\n"
"This method is a convenience method that gets all user properties as a single hash.\n"
"\n"
"This method has been introduced in version 0.29.5."
) +
method_ext ("[]", &inst_index, gsi::arg ("key"),
"@brief Gets the user property with the given key or, if available, the PCell parameter with the name given by the key\n"
"Getting the PCell parameter has priority over the user property."

View File

@ -41,6 +41,7 @@
#include "dbLayerMapping.h"
#include "dbCellMapping.h"
#include "dbTechnology.h"
#include "dbLayoutUtils.h"
#include "tlStream.h"
#include "tlGlobPattern.h"
@ -343,7 +344,7 @@ static void set_layout_property (db::Layout *l, const tl::Variant &key, const tl
l->prop_id (l->properties_repository ().properties_id (props));
}
static tl::Variant get_layout_property (db::Layout *l, const tl::Variant &key)
static tl::Variant get_layout_property (const db::Layout *l, const tl::Variant &key)
{
// TODO: check if is editable
@ -366,6 +367,21 @@ static tl::Variant get_layout_property (db::Layout *l, const tl::Variant &key)
}
}
static tl::Variant get_layout_properties (const db::Layout *layout)
{
db::properties_id_type id = layout->prop_id ();
if (id == 0) {
return tl::Variant::empty_array ();
}
tl::Variant res = tl::Variant::empty_array ();
const db::PropertiesRepository::properties_set &props = layout->properties_repository ().properties (id);
for (auto i = props.begin (); i != props.end (); ++i) {
res.insert (layout->properties_repository ().prop_name (i->first), i->second);
}
return res;
}
static db::cell_index_type cell_by_name (db::Layout *l, const char *name)
{
std::pair<bool, db::cell_index_type> c = l->cell_by_name (name);
@ -1050,6 +1066,16 @@ static void set_properties (db::Layout *layout, unsigned int index, const db::La
}
}
void break_polygons2 (db::Layout *layout, unsigned int layer, size_t max_vertex_count, double max_area_ratio)
{
db::break_polygons (*layout, layer, max_vertex_count, max_area_ratio);
}
void break_polygons1 (db::Layout *layout, size_t max_vertex_count, double max_area_ratio)
{
db::break_polygons (*layout, max_vertex_count, max_area_ratio);
}
Class<db::Layout> decl_Layout ("db", "Layout",
gsi::constructor ("new", &layout_ctor_with_manager, gsi::arg ("manager"),
"@brief Creates a layout object attached to a manager\n"
@ -1254,6 +1280,12 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"\n"
"This method has been introduced in version 0.24."
) +
gsi::method_ext ("properties", &get_layout_properties,
"@brief Gets the user properties as a hash\n"
"This method is a convenience method that gets all user properties as a single hash.\n"
"\n"
"This method has been introduced in version 0.29.5."
) +
gsi::method_ext ("properties_id", &properties_id, gsi::arg ("properties"),
"@brief Gets the properties ID for a given properties set\n"
"\n"
@ -1956,6 +1988,33 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"\n"
"This method has been introduced in version 0.26.1.\n"
) +
gsi::method_ext ("break_polygons", &break_polygons1, gsi::arg ("max_vertex_count"), gsi::arg ("max_area_ratio"),
"@brief Breaks the polygons of the layout into smaller ones\n"
"\n"
"There are two criteria for splitting a polygon: a polygon is split into parts with less then "
"'max_vertex_count' points and an bounding box-to-polygon area ratio less than 'max_area_ratio'. "
"The area ratio is supposed to render polygons whose bounding box is a better approximation. "
"This applies for example to 'L' shape polygons.\n"
"\n"
"Using a value of 0 for either limit means that the respective limit isn't checked. "
"Breaking happens by cutting the polygons into parts at 'good' locations. The "
"algorithm does not have a specific goal to minimize the number of parts for example. "
"The only goal is to achieve parts within the given limits.\n"
"\n"
"Breaking also applies to paths if their polygon representation satisfies the breaking criterion. "
"In that case, paths are converted to polygons and broken into smaller parts.\n"
"\n"
"This variant applies breaking to all cells and layers.\n"
"\n"
"This method has been introduced in version 0.29.5."
) +
gsi::method_ext ("break_polygons", &break_polygons2, gsi::arg ("layer"), gsi::arg ("max_vertex_count"), gsi::arg ("max_area_ratio"),
"@brief Breaks the polygons of the layer into smaller ones\n"
"\n"
"This variant applies breaking to all cells and the given layer.\n"
"\n"
"This method has been introduced in version 0.29.5."
) +
gsi::method ("transform", (void (db::Layout::*) (const db::Trans &t)) &db::Layout::transform, gsi::arg ("trans"),
"@brief Transforms the layout with the given transformation\n"
"\n"

View File

@ -2044,6 +2044,32 @@ Class<db::Netlist> decl_dbNetlist ("db", "Netlist",
"\n"
"This method has been introduced in version 0.28.4.\n"
) +
gsi::method ("top_circuit", static_cast<db::Circuit *(db::Netlist::*) ()> (&db::Netlist::top_circuit),
"@brief Gets the top circuit.\n"
"This method will return nil, if there is no top circuit. It will raise an error, if there is more than "
"a single top circuit.\n"
"\n"
"This convenience method has been added in version 0.29.5."
) +
gsi::method ("top_circuit", static_cast<const db::Circuit *(db::Netlist::*) () const> (&db::Netlist::top_circuit),
"@brief Gets the top circuit (const version).\n"
"This method will return nil, if there is no top circuit. It will raise an error, if there is more than "
"a single top circuit.\n"
"\n"
"This convenience method has been added in version 0.29.5."
) +
gsi::method ("top_circuits", static_cast<std::vector<db::Circuit *> (db::Netlist::*) ()> (&db::Netlist::top_circuits),
"@brief Gets the top circuits.\n"
"Returns a list of top circuits.\n"
"\n"
"This convenience method has been added in version 0.29.5."
) +
gsi::method ("top_circuits", static_cast<std::vector<const db::Circuit *> (db::Netlist::*) () const> (&db::Netlist::top_circuits),
"@brief Gets the top circuits.\n"
"Returns a list of top circuits.\n"
"\n"
"This convenience method has been added in version 0.29.5."
) +
gsi::method_ext ("nets_by_name", &nets_by_name_const_from_netlist, gsi::arg ("name_pattern"),
"@brief Gets the net objects for a given name filter (const version).\n"
"The name filter is a glob pattern. This method will return all \\Net objects matching the glob pattern.\n"

View File

@ -1132,6 +1132,24 @@ rasterize1 (const db::Region *region, const db::Point &origin, const db::Vector
return rasterize2 (region, origin, pixel_size, pixel_size, nx, ny);
}
static tl::Variant begin_shapes_rec (const db::Region *region)
{
auto res = region->begin_iter ();
tl::Variant r = tl::Variant (std::vector<tl::Variant> ());
r.push (tl::Variant (res.first));
r.push (tl::Variant (res.second));
return r;
}
static tl::Variant begin_merged_shapes_rec (const db::Region *region)
{
auto res = region->begin_merged_iter ();
tl::Variant r = tl::Variant (std::vector<tl::Variant> ());
r.push (tl::Variant (res.first));
r.push (tl::Variant (res.second));
return r;
}
static db::Point default_origin;
// provided by gsiDeclDbPolygon.cc:
@ -2895,7 +2913,7 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
"This method returns all polygons in self which are not rectilinear."
"Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n"
) +
method_ext ("break", &break_polygons, gsi::arg ("max_vertex_count"), gsi::arg ("max_area_ratio", 0.0),
method_ext ("break_polygons|#break", &break_polygons, gsi::arg ("max_vertex_count"), gsi::arg ("max_area_ratio", 0.0),
"@brief Breaks the polygons of the region into smaller ones\n"
"\n"
"There are two criteria for splitting a polygon: a polygon is split into parts with less then "
@ -2908,7 +2926,8 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
"algorithm does not have a specific goal to minimize the number of parts for example. "
"The only goal is to achieve parts within the given limits.\n"
"\n"
"This method has been introduced in version 0.26."
"This method has been introduced in version 0.26. The 'break_polygons' alias has been introduced "
"in version 0.29.5 to avoid issues with reserved keywords."
) +
method_ext ("delaunay", &delaunay,
"@brief Computes a constrained Delaunay triangulation from the given region\n"
@ -2932,6 +2951,8 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
"and is usually a good choice. The algorithm is stable up to roughly 1.2 which corresponds to "
"a minimum angle of abouth 37 degree.\n"
"\n"
"The minimum angle of the resulting triangles relates to the 'b' parameter as: @t min_angle = arcsin(B/2) @/t.\n"
"\n"
"The area value is given in terms of DBU units. Picking a value of 0.0 for area and min b will "
"make the implementation skip the refinement step. In that case, the results are identical to "
"the standard constrained Delaunay triangulation.\n"
@ -3756,7 +3777,32 @@ Class<db::Region> decl_Region (decl_dbShapeCollection, "db", "Region",
"metal1_all_nets = metal1.nets\n"
"@/code\n"
"\n"
"This method was introduced in version 0.28.4"
"This method was introduced in version 0.28.4."
) +
gsi::method_ext ("begin_shapes_rec", &begin_shapes_rec,
"@brief Returns a recursive shape iterator plus a transformation for the shapes constituting this region.\n"
"This method returns a pair consisting of a \\RecursiveShapeIterator plus a \\ICplxTrans transformation. "
"Both objects allow accessing the shapes (polygons) of the region in a detailed fashion. To iterate the "
"the polygons use a code like this:\n"
"\n"
"@code\n"
"iter, trans = region.begin_shapes_rec\n"
"iter.each do |i|\n"
" polygon = trans * iter.trans * i.shape.polygon\n"
" ...\n"
"end\n"
"@/code\n"
"\n"
"This method is the most powerful way of accessing the shapes inside the region. I allows for example to obtain the "
"properties attached to the polygons of the region. It is primarily intended for special applications like iterating net-annotated shapes.\n"
"\n"
"This speciality method was introduced in version 0.29.5."
) +
gsi::method_ext ("begin_merged_shapes_rec", &begin_merged_shapes_rec,
"@brief Returns a recursive shape iterator plus a transformation for the shapes constituting the merged region.\n"
"It can be used like \\begin_shapes_rec, but delivers shapes from the merged polygons pool.\n"
"\n"
"This speciality method was introduced in version 0.29.5."
) +
gsi::make_property_methods<db::Region> ()
,

View File

@ -1004,6 +1004,26 @@ static tl::Variant get_property (const db::Shape *s, const tl::Variant &key)
}
}
static tl::Variant get_properties (const db::Shape *s)
{
db::properties_id_type id = s->prop_id ();
if (id == 0) {
return tl::Variant::empty_array ();
}
const db::Layout *layout = layout_ptr_const (s);
if (! layout) {
throw tl::Exception (tl::to_string (tr ("Shape does not reside inside a layout - cannot retrieve properties")));
}
tl::Variant res = tl::Variant::empty_array ();
const db::PropertiesRepository::properties_set &props = layout->properties_repository ().properties (id);
for (auto i = props.begin (); i != props.end (); ++i) {
res.insert (layout->properties_repository ().prop_name (i->first), i->second);
}
return res;
}
namespace
{
@ -1354,6 +1374,12 @@ Class<db::Shape> decl_Shape ("db", "Shape",
"\n"
"This method has been introduced in version 0.22."
) +
gsi::method_ext ("properties", &get_properties,
"@brief Gets the user properties\n"
"This method is a convenience method that gets the properties of the shape as a single hash.\n"
"\n"
"This method has been introduced in version 0.29.5."
) +
gsi::iterator ("each_point", &db::Shape::begin_point, &db::Shape::end_point,
"@brief Iterates over all points of the object\n"
"\n"

View File

@ -30,6 +30,7 @@
#include "dbRegion.h"
#include "dbEdgePairs.h"
#include "dbEdges.h"
#include "dbLayoutUtils.h"
namespace gsi
{
@ -441,6 +442,11 @@ static db::Layout *layout (db::Shapes *sh)
}
}
static void break_polygons (db::Shapes *sh, size_t max_vertex_count, double max_area_ratio)
{
db::break_polygons (*sh, max_vertex_count, max_area_ratio);
}
static unsigned int s_all () { return db::ShapeIterator::All; }
static unsigned int s_all_with_properties () { return db::ShapeIterator::AllWithProperties; }
static unsigned int s_properties () { return db::ShapeIterator::Properties; }
@ -791,6 +797,24 @@ Class<db::Shapes> decl_Shapes ("db", "Shapes",
"\n"
"This method has been introduced in version 0.25.\n"
) +
gsi::method_ext ("break_polygons", &break_polygons, gsi::arg ("max_vertex_count"), gsi::arg ("max_area_ratio", 0.0),
"@brief Breaks the polygons of the shape container into smaller ones\n"
"\n"
"There are two criteria for splitting a polygon: a polygon is split into parts with less then "
"'max_vertex_count' points and an bounding box-to-polygon area ratio less than 'max_area_ratio'. "
"The area ratio is supposed to render polygons whose bounding box is a better approximation. "
"This applies for example to 'L' shape polygons.\n"
"\n"
"Using a value of 0 for either limit means that the respective limit isn't checked. "
"Breaking happens by cutting the polygons into parts at 'good' locations. The "
"algorithm does not have a specific goal to minimize the number of parts for example. "
"The only goal is to achieve parts within the given limits.\n"
"\n"
"Breaking also applies to paths if their polygon representation satisfies the breaking criterion. "
"In that case, paths are converted to polygons and broken into smaller parts.\n"
"\n"
"This method has been introduced in version 0.29.5."
) +
gsi::method_ext ("replace", &replace<db::Box>, gsi::arg ("shape"), gsi::arg ("box"),
"@brief Replaces the given shape with a box\n"
"@return A reference to the new shape (a \\Shape object)\n"

View File

@ -779,3 +779,42 @@ TEST(20_scale_and_snap)
db::compare_layouts (_this, l1, tl::testdata () + "/algo/layout_utils_au_sns4.oas", db::NormalizationMode (db::WriteOAS + db::WithArrays));
}
TEST(21_break1)
{
db::Layout l1;
{
std::string fn (tl::testdata ());
fn += "/algo/break_polygons_test.gds";
tl::InputStream stream (fn);
db::Reader reader (stream);
reader.read (l1);
}
db::break_polygons (l1, 10, 3.0);
CHECKPOINT();
db::compare_layouts (_this, l1, tl::testdata () + "/algo/layout_utils_au_bp1.gds");
}
TEST(22_break2)
{
db::Layout l1;
{
std::string fn (tl::testdata ());
fn += "/algo/break_polygons_test.gds";
tl::InputStream stream (fn);
db::Reader reader (stream);
reader.read (l1);
}
unsigned int li1 = find_layer (l1, 1, 0);
unsigned int li2 = find_layer (l1, 2, 0);
db::break_polygons (l1, li1, 10, 0.0);
db::break_polygons (l1, li2, 0, 3.0);
CHECKPOINT();
db::compare_layouts (_this, l1, tl::testdata () + "/algo/layout_utils_au_bp2.gds");
}

View File

@ -914,6 +914,25 @@ TEST(25_dismiss_top_level)
);
}
TEST(26_comments)
{
db::Netlist nl;
std::string path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader26.cir");
db::NetlistSpiceReader reader;
tl::InputStream is (path);
reader.read (is, nl);
EXPECT_EQ (nl.to_string (),
"circuit TOP (A=A,B=B);\n"
" device NMOS '1' (S=A,G=B,D=A,B=B) (L=100,W=100,AS=0,AD=0,PS=0,PD=0);\n"
" device NMOS '2' (S='\\\\A',G='NET\"Q\"',D=A,B='NET[0]') (L=100,W=100,AS=0,AD=0,PS=0,PD=0);\n"
" device NMOS 'FET[1]' (S='\\\\A',G='NET\"Q\"',D=AAA,B='NET[0]') (L=100,W=1.7,AS=0,AD=0,PS=0,PD=0);\n"
"end;\n"
);
}
TEST(100_ExpressionParser)
{
std::map<std::string, tl::Variant> vars;

View File

@ -208,6 +208,18 @@ static std::string parents2string (const db::Circuit *c)
return res;
}
static std::string td2string_nc (db::Netlist *nl)
{
std::string res;
for (db::Netlist::top_down_circuit_iterator r = nl->begin_top_down (); r != nl->end_top_down (); ++r) {
if (!res.empty ()) {
res += ",";
}
res += r->name ();
}
return res;
}
static std::string td2string (const db::Netlist *nl)
{
std::string res;
@ -220,6 +232,64 @@ static std::string td2string (const db::Netlist *nl)
return res;
}
static std::string tcs2string_nc (db::Netlist *nl)
{
std::string res;
std::vector<db::Circuit *> tops = nl->top_circuits ();
for (auto i = tops.begin (); i != tops.end (); ++i) {
if (!res.empty ()) {
res += ",";
}
res += (*i)->name ();
}
return res;
}
static std::string tcs2string (const db::Netlist *nl)
{
std::string res;
std::vector<const db::Circuit *> tops = nl->top_circuits ();
for (auto i = tops.begin (); i != tops.end (); ++i) {
if (!res.empty ()) {
res += ",";
}
res += (*i)->name ();
}
return res;
}
static std::string tc2string_nc (db::Netlist *nl)
{
const db::Circuit *tc = nl->top_circuit ();
if (!tc) {
return "(nil)";
} else {
return tc->name ();
}
}
static std::string tc2string (const db::Netlist *nl)
{
const db::Circuit *tc = nl->top_circuit ();
if (!tc) {
return "(nil)";
} else {
return tc->name ();
}
}
static std::string bu2string_nc (db::Netlist *nl)
{
std::string res;
for (db::Netlist::bottom_up_circuit_iterator r = nl->begin_bottom_up (); r != nl->end_bottom_up (); ++r) {
if (!res.empty ()) {
res += ",";
}
res += r->name ();
}
return res;
}
static std::string bu2string (const db::Netlist *nl)
{
std::string res;
@ -1038,20 +1108,44 @@ TEST(12_NetlistTopology)
{
std::unique_ptr<db::Netlist> nl (new db::Netlist ());
EXPECT_EQ (nl->top_circuit_count (), size_t (0));
EXPECT_EQ (tcs2string (nl.get ()), "");
EXPECT_EQ (tcs2string_nc (nl.get ()), "");
EXPECT_EQ (tc2string (nl.get ()), "(nil)");
EXPECT_EQ (tc2string_nc (nl.get ()), "(nil)");
db::Circuit *c1 = new db::Circuit ();
c1->set_name ("c1");
nl->add_circuit (c1);
EXPECT_EQ (nl->top_circuit_count (), size_t (1));
EXPECT_EQ (td2string (nl.get ()), "c1");
EXPECT_EQ (td2string_nc (nl.get ()), "c1");
EXPECT_EQ (tcs2string (nl.get ()), "c1");
EXPECT_EQ (tcs2string_nc (nl.get ()), "c1");
EXPECT_EQ (tc2string (nl.get ()), "c1");
EXPECT_EQ (tc2string_nc (nl.get ()), "c1");
EXPECT_EQ (bu2string (nl.get ()), "c1");
EXPECT_EQ (bu2string_nc (nl.get ()), "c1");
db::Circuit *c2 = new db::Circuit ();
c2->set_name ("c2");
nl->add_circuit (c2);
EXPECT_EQ (nl->top_circuit_count (), size_t (2));
EXPECT_EQ (td2string (nl.get ()), "c2,c1");
EXPECT_EQ (td2string_nc (nl.get ()), "c2,c1");
EXPECT_EQ (tcs2string (nl.get ()), "c2,c1");
EXPECT_EQ (tcs2string_nc (nl.get ()), "c2,c1");
try {
tc2string (nl.get ());
EXPECT_EQ (true, false);
} catch (...) {
}
try {
tc2string_nc (nl.get ());
EXPECT_EQ (true, false);
} catch (...) {
}
EXPECT_EQ (bu2string (nl.get ()), "c1,c2");
EXPECT_EQ (bu2string_nc (nl.get ()), "c1,c2");
std::unique_ptr<db::NetlistLocker> locker (new db::NetlistLocker (nl.get ()));

View File

@ -26,6 +26,7 @@
#include "dbLayout.h"
#include "edtDialogs.h"
#include "edtUtils.h"
#include "layObjectInstPath.h"
#include "layCellView.h"
#include "layLayoutViewBase.h"
@ -79,29 +80,39 @@ InstantiationForm::double_clicked (QListWidgetItem *item)
return;
}
lay::CellView::unspecific_cell_path_type path (mp_view->cellview (mp_path->cv_index ()).combined_unspecific_path ());
int cv_index = mp_path->cv_index ();
const lay::CellView &cv = mp_view->cellview (cv_index);
lay::CellView::unspecific_cell_path_type path (cv.combined_unspecific_path ());
int nrow = 0;
for (lay::CellView::specific_cell_path_type::const_iterator p = cv.specific_path ().begin (); p != cv.specific_path ().end () && nrow < row; ++p, ++nrow) {
path.push_back (p->inst_ptr.cell_index ());
}
for (lay::ObjectInstPath::iterator p = mp_path->begin (); p != mp_path->end () && nrow < row; ++p, ++nrow) {
path.push_back (p->inst_ptr.cell_index ());
}
mp_view->set_current_cell_path (mp_path->cv_index (), path);
mp_view->set_current_cell_path (cv_index, path);
if (! mp_marker) {
mp_marker = new lay::Marker (mp_view, mp_path->cv_index ());
mp_marker = new lay::Marker (mp_view, cv_index);
}
const db::Layout &layout = mp_view->cellview (mp_path->cv_index ())->layout ();
db::Box box = layout.cell (row == 0 ? mp_path->topcell () : path.back ()).bbox ();
const db::Layout &layout = cv->layout ();
db::Box box = layout.cell (row == 0 ? cv.ctx_cell_index (): path.back ()).bbox ();
// TODO: this does not consider global transformation and variants of this
db::ICplxTrans abs_trans;
nrow = 0;
for (lay::CellView::specific_cell_path_type::const_iterator p = cv.specific_path ().begin (); p != cv.specific_path ().end () && nrow < row; ++p, ++nrow) {
abs_trans = abs_trans * p->complex_trans ();
}
for (lay::ObjectInstPath::iterator p = mp_path->begin (); p != mp_path->end () && nrow < row; ++p, ++nrow) {
abs_trans = abs_trans * (p->inst_ptr.cell_inst ().complex_trans (*(p->array_inst)));
abs_trans = abs_trans * p->complex_trans ();
}
mp_marker->set (box, abs_trans, mp_view->cv_transform_variants (mp_path->cv_index ()));
mp_marker->set (box, abs_trans, mp_view->cv_transform_variants (cv_index));
}
void
@ -136,14 +147,15 @@ InstantiationForm::update ()
list->clear ();
list->addItem (tl::to_qstring (cv->layout ().cell_name (cv.ctx_cell_index ())));
db::CplxTrans abs_trans;
db::ICplxTrans abs_trans;
// first include the context path of the cellview in order to tell the path within the cell shown
for (lay::CellView::specific_cell_path_type::const_iterator p = cv.specific_path ().begin (); p != cv.specific_path ().end (); ++p) {
// build the instance information from the path
db::CplxTrans trans (p->inst_ptr.cell_inst ().complex_trans (*(p->array_inst)));
db::ICplxTrans trans = p->complex_trans ();
size_t n = p->array_inst.at_end () ? p->inst_ptr.cell_inst ().size () : size_t (1);
abs_trans = abs_trans * trans;
if (abs_coord) {
@ -152,8 +164,11 @@ InstantiationForm::update ()
std::string line;
line += cv->layout ().cell_name (p->inst_ptr.cell_index ());
line += "\tat ";
line += "\t" + tl::to_string (tr ("at")) + " ";
line += trans.to_string (true /*lazy*/, dbu_coord ? 0.0 : dbu);
if (n > 1) {
line += " " + tl::sprintf (tl::to_string (tr ("(first of %lu array members)")), (unsigned long) n);
}
list->addItem (tl::to_qstring (line));
@ -164,7 +179,8 @@ InstantiationForm::update ()
// build the instance information from the path
db::CplxTrans trans (p->inst_ptr.cell_inst ().complex_trans (*(p->array_inst)));
db::ICplxTrans trans = p->complex_trans ();
size_t n = p->array_inst.at_end () ? p->inst_ptr.cell_inst ().size () : size_t (1);
abs_trans = abs_trans * trans;
if (abs_coord) {
@ -173,9 +189,12 @@ InstantiationForm::update ()
std::string line;
line += cv->layout ().cell_name (p->inst_ptr.cell_index ());
line += "\tat ";
line += "\t" + tl::to_string (tr ("at")) + " ";
line += trans.to_string (true /*lazy*/, dbu_coord ? 0.0 : dbu);
if (n > 1) {
line += " " + tl::sprintf (tl::to_string (tr ("(first of %lu array members)")), (unsigned long) n);
}
list->addItem (tl::to_qstring (line));
}

View File

@ -47,7 +47,7 @@ namespace edt
// -------------------------------------------------------------------------
static std::string cell_name_from_sel (const edt::Service::obj_iterator &pos, edt::Service *service)
static std::string cell_name_from_sel (EditableSelectionIterator::pointer pos, edt::Service *service)
{
if (! pos->is_cell_inst ()) {
return std::string ();
@ -81,7 +81,7 @@ struct SelectionPtrSort
// .. nothing yet ..
}
bool operator() (const edt::Service::obj_iterator &a, const edt::Service::obj_iterator &b)
bool operator() (EditableSelectionIterator::pointer a, EditableSelectionIterator::pointer b)
{
return cell_name_from_sel (a, mp_service) < cell_name_from_sel (b, mp_service);
}
@ -104,10 +104,9 @@ static bool is_orthogonal (const db::DVector &rv, const db::DVector &cv)
InstPropertiesPage::InstPropertiesPage (edt::Service *service, db::Manager *manager, QWidget *parent)
: lay::PropertiesPage (parent, manager, service), mp_service (service), m_enable_cb_callback (true), mp_pcell_parameters (0)
{
const edt::Service::objects &selection = service->selection ();
m_selection_ptrs.reserve (selection.size ());
for (edt::Service::obj_iterator s = selection.begin (); s != selection.end (); ++s) {
m_selection_ptrs.push_back (s);
m_selection_ptrs.reserve (service->selection_size ());
for (EditableSelectionIterator s = service->begin_selection (); ! s.at_end (); ++s) {
m_selection_ptrs.push_back (s.operator-> ());
}
std::sort (m_selection_ptrs.begin (), m_selection_ptrs.end (), SelectionPtrSort (service));
@ -221,7 +220,7 @@ BEGIN_PROTECTED
lib = lib_cbx->current_library ();
layout = &lib->layout ();
} else {
edt::Service::obj_iterator pos = m_selection_ptrs [m_indexes.front ()];
EditableSelectionIterator::pointer pos = m_selection_ptrs [m_indexes.front ()];
const lay::CellView &cv = mp_service->view ()->cellview (pos->cv_index ());
layout = &cv->layout ();
}
@ -300,7 +299,7 @@ InstPropertiesPage::select_entries (const std::vector<size_t> &entries)
std::string
InstPropertiesPage::description (size_t entry) const
{
edt::Service::obj_iterator pos = m_selection_ptrs [entry];
EditableSelectionIterator::pointer pos = m_selection_ptrs [entry];
std::string d = cell_name_from_sel (pos, mp_service);
if (! pos->is_cell_inst ()) {
@ -343,12 +342,12 @@ InstPropertiesPage::update ()
return;
}
edt::Service::obj_iterator pos = m_selection_ptrs [m_indexes.front ()];
EditableSelectionIterator::pointer pos = m_selection_ptrs [m_indexes.front ()];
tl_assert (pos->is_cell_inst ());
std::set<const lay::ObjectInstPath *> highlights;
for (auto i = m_indexes.begin (); i != m_indexes.end (); ++i) {
highlights.insert (m_selection_ptrs [*i].operator-> ());
highlights.insert (m_selection_ptrs [*i]);
}
mp_service->highlight (highlights);
@ -472,7 +471,7 @@ InstPropertiesPage::show_cell ()
return;
}
edt::Service::obj_iterator pos = m_selection_ptrs [m_indexes.front ()];
EditableSelectionIterator::pointer pos = m_selection_ptrs [m_indexes.front ()];
lay::CellView::unspecific_cell_path_type path (mp_service->view ()->cellview (pos->cv_index ()).combined_unspecific_path ());
for (lay::ObjectInstPath::iterator p = pos->begin (); p != pos->end (); ++p) {
@ -509,7 +508,7 @@ InstPropertiesPage::create_applicator (db::Cell & /*cell*/, const db::Instance &
std::unique_ptr<CombinedChangeApplicator> appl (new CombinedChangeApplicator ());
edt::Service::obj_iterator pos = m_selection_ptrs [m_indexes.front ()];
EditableSelectionIterator::pointer pos = m_selection_ptrs [m_indexes.front ()];
const lay::CellView &cv = mp_service->view ()->cellview (pos->cv_index ());
bool du = dbu_cb->isChecked ();
@ -786,16 +785,15 @@ InstPropertiesPage::create_applicator (db::Cell & /*cell*/, const db::Instance &
void
InstPropertiesPage::recompute_selection_ptrs (const std::vector<lay::ObjectInstPath> &new_sel)
{
std::map<lay::ObjectInstPath, edt::Service::obj_iterator> ptrs;
std::map<lay::ObjectInstPath, EditableSelectionIterator::pointer> ptrs;
const edt::Service::objects &selection = mp_service->selection ();
for (edt::Service::obj_iterator pos = selection.begin (); pos != selection.end (); ++pos) {
ptrs.insert (std::make_pair (*pos, pos));
for (EditableSelectionIterator pos = mp_service->begin_selection (); ! pos.at_end (); ++pos) {
ptrs.insert (std::make_pair (*pos, pos.operator -> ()));
}
m_selection_ptrs.clear ();
for (std::vector<lay::ObjectInstPath>::const_iterator s = new_sel.begin (); s != new_sel.end (); ++s) {
std::map<lay::ObjectInstPath, edt::Service::obj_iterator>::const_iterator pm = ptrs.find (*s);
std::map<lay::ObjectInstPath, EditableSelectionIterator::pointer>::const_iterator pm = ptrs.find (*s);
tl_assert (pm != ptrs.end ());
m_selection_ptrs.push_back (pm->second);
}
@ -814,7 +812,7 @@ InstPropertiesPage::do_apply (bool current_only, bool relative)
std::unique_ptr<ChangeApplicator> applicator;
{
edt::Service::obj_iterator pos = m_selection_ptrs [m_indexes.front ()];
EditableSelectionIterator::pointer pos = m_selection_ptrs [m_indexes.front ()];
tl_assert (pos->is_cell_inst ());
const lay::CellView &cv = mp_service->view ()->cellview (pos->cv_index ());
@ -846,7 +844,7 @@ InstPropertiesPage::do_apply (bool current_only, bool relative)
std::vector<lay::ObjectInstPath> new_sel;
new_sel.reserve (m_selection_ptrs.size ());
for (std::vector<edt::Service::obj_iterator>::const_iterator p = m_selection_ptrs.begin (); p != m_selection_ptrs.end (); ++p) {
for (std::vector<EditableSelectionIterator::pointer>::const_iterator p = m_selection_ptrs.begin (); p != m_selection_ptrs.end (); ++p) {
new_sel.push_back (**p);
}
@ -859,7 +857,7 @@ InstPropertiesPage::do_apply (bool current_only, bool relative)
for (auto ii = m_indexes.begin (); ii != m_indexes.end (); ++ii) {
size_t index = *ii;
edt::Service::obj_iterator pos = m_selection_ptrs [*ii];
EditableSelectionIterator::pointer pos = m_selection_ptrs [*ii];
// only update objects from the same layout - this is not practical limitation but saves a lot of effort for
// managing different property id's etc.
@ -958,7 +956,7 @@ InstPropertiesPage::update_pcell_parameters ()
} else {
edt::Service::obj_iterator pos = m_selection_ptrs [m_indexes.front ()];
EditableSelectionIterator::pointer pos = m_selection_ptrs [m_indexes.front ()];
const lay::CellView &cv = mp_service->view ()->cellview (pos->cv_index ());
layout = &cv->layout ();
@ -982,7 +980,7 @@ InstPropertiesPage::update_pcell_parameters ()
std::vector<tl::Variant> parameters;
edt::Service::obj_iterator pos = m_selection_ptrs [m_indexes.front ()];
EditableSelectionIterator::pointer pos = m_selection_ptrs [m_indexes.front ()];
const lay::CellView &cv = mp_service->view ()->cellview (pos->cv_index ());
db::Cell &cell = cv->layout ().cell (pos->cell_index ());
std::pair<bool, db::pcell_id_type> pci = cell.is_pcell_instance (pos->back ().inst_ptr);

View File

@ -58,7 +58,7 @@ private:
void recompute_selection_ptrs (const std::vector<lay::ObjectInstPath> &new_sel);
protected:
std::vector<edt::Service::obj_iterator> m_selection_ptrs;
std::vector<EditableSelectionIterator::pointer> m_selection_ptrs;
std::vector<size_t> m_indexes;
edt::Service *mp_service;
bool m_enable_cb_callback;

View File

@ -310,8 +310,7 @@ MainService::cm_descend ()
std::vector<edt::Service *> edt_services = view ()->get_plugins <edt::Service> ();
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end () && common_inst.valid (); ++es) {
const edt::Service::objects &selection = (*es)->selection ();
for (edt::Service::objects::const_iterator sel = selection.begin (); sel != selection.end () && common_inst.valid (); ++sel) {
for (EditableSelectionIterator sel = (*es)->begin_selection (); ! sel.at_end () && common_inst.valid (); ++sel) {
common_inst.add (*sel, 1);
}
}
@ -335,12 +334,10 @@ MainService::cm_descend ()
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
const edt::Service::objects &selection = (*es)->selection ();
new_selections.push_back (std::vector<lay::ObjectInstPath> ());
new_selections.back ().reserve (selection.size ());
new_selections.back ().reserve ((*es)->selection_size ());
for (edt::Service::objects::const_iterator sel = selection.begin (); sel != selection.end (); ++sel) {
for (EditableSelectionIterator sel = (*es)->begin_selection (); ! sel.at_end (); ++sel) {
new_selections.back ().push_back (*sel);
lay::ObjectInstPath &new_sel = new_selections.back ().back ();
@ -378,7 +375,14 @@ MainService::cm_ascend ()
new_selections.reserve (edt_services.size ());
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
new_selections.push_back (std::vector<lay::ObjectInstPath> ());
new_selections.back ().insert (new_selections.back ().end (), (*es)->selection ().begin (), (*es)->selection ().end ());
size_t n = 0;
for (EditableSelectionIterator i = (*es)->begin_selection (); ! i.at_end (); ++i) {
++n;
}
new_selections.back ().reserve (n);
for (EditableSelectionIterator i = (*es)->begin_selection (); ! i.at_end (); ++i) {
new_selections.back ().push_back (*i);
}
}
// this will clear the selection:
@ -439,8 +443,7 @@ MainService::cm_flatten_insts ()
std::vector<edt::Service *> edt_services = view ()->get_plugins <edt::Service> ();
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
const edt::Service::objects &sel = (*es)->selection ();
for (edt::Service::objects::const_iterator r = sel.begin (); r != sel.end (); ++r) {
for (EditableSelectionIterator r = (*es)->begin_selection (); ! r.at_end (); ++r) {
const lay::CellView &cv = view ()->cellview (r->cv_index ());
if (cv.is_valid ()) {
@ -494,12 +497,10 @@ MainService::cm_move_hier_up ()
std::vector<edt::Service *> edt_services = view ()->get_plugins <edt::Service> ();
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
const edt::Service::objects &selection = (*es)->selection ();
std::vector<lay::ObjectInstPath> new_selection;
new_selection.reserve (selection.size ());
new_selection.reserve ((*es)->selection_size ());
for (edt::Service::objects::const_iterator r = selection.begin (); r != selection.end (); ++r) {
for (EditableSelectionIterator r = (*es)->begin_selection (); ! r.at_end (); ++r) {
const lay::CellView &cv = view ()->cellview (r->cv_index ());
if (cv.is_valid ()) {
@ -731,8 +732,7 @@ MainService::cm_make_cell_variants ()
// TODO: this limitation is not really necessary, but makes the code somewhat simpler
int cv_index = -1;
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
const edt::Service::objects &selection = (*es)->selection ();
for (edt::Service::objects::const_iterator r = selection.begin (); r != selection.end (); ++r) {
for (EditableSelectionIterator r = (*es)->begin_selection (); ! r.at_end (); ++r) {
if (cv_index < 0) {
cv_index = r->cv_index ();
} else if (cv_index != int (r->cv_index ())) {
@ -755,8 +755,15 @@ MainService::cm_make_cell_variants ()
}
std::vector<lay::ObjectInstPath> new_selection;
size_t n = 0;
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
new_selection.insert (new_selection.end (), (*es)->selection ().begin (), (*es)->selection ().end ());
n += (*es)->selection_size ();
}
new_selection.reserve (n);
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
for (EditableSelectionIterator s = (*es)->begin_selection (); ! s.at_end (); ++s) {
new_selection.push_back (*s);
}
}
size_t num_sel = new_selection.size ();
@ -916,7 +923,7 @@ MainService::cm_make_cell_variants ()
// Install the new selection
size_t i0 = 0;
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
size_t n = (*es)->selection ().size ();
size_t n = (*es)->selection_size ();
if (n + i0 <= new_selection.size ()) {
(*es)->set_selection (new_selection.begin () + i0, new_selection.begin () + i0 + n);
}
@ -944,8 +951,7 @@ MainService::cm_resolve_arefs ()
int cv_index = -1;
const edt::Service::objects &selection = inst_service->selection ();
for (edt::Service::objects::const_iterator r = selection.begin (); r != selection.end (); ++r) {
for (EditableSelectionIterator r = inst_service->begin_selection (); ! r.at_end (); ++r) {
if (r->is_cell_inst () && r->back ().inst_ptr.size () > 1) {
if (cv_index < 0) {
cv_index = r->cv_index ();
@ -1026,8 +1032,7 @@ MainService::cm_make_cell ()
int cv_index = -1;
std::vector<edt::Service *> edt_services = view ()->get_plugins <edt::Service> ();
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
const edt::Service::objects &selection = (*es)->selection () ;
for (edt::Service::objects::const_iterator r = selection.begin (); r != selection.end (); ++r) {
for (EditableSelectionIterator r = (*es)->begin_selection (); ! r.at_end (); ++r) {
if (cv_index < 0) {
cv_index = r->cv_index ();
} else if (cv_index != int (r->cv_index ())) {
@ -1051,8 +1056,7 @@ MainService::cm_make_cell ()
db::Box selection_bbox;
db::box_convert<db::CellInst> bc (cv->layout ());
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
const edt::Service::objects &selection = (*es)->selection () ;
for (edt::Service::objects::const_iterator r = selection.begin (); r != selection.end (); ++r) {
for (EditableSelectionIterator r = (*es)->begin_selection (); ! r.at_end (); ++r) {
if (r->is_cell_inst ()) {
selection_bbox += db::ICplxTrans (r->trans ()) * r->back ().bbox (bc);
} else {
@ -1086,8 +1090,7 @@ MainService::cm_make_cell ()
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
const edt::Service::objects &selection = (*es)->selection () ;
for (edt::Service::objects::const_iterator r = selection.begin (); r != selection.end (); ++r) {
for (EditableSelectionIterator r = (*es)->begin_selection (); ! r.at_end (); ++r) {
if (r->is_cell_inst ()) {
@ -1148,8 +1151,7 @@ MainService::cm_convert_to_cell ()
// Do the conversion
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
const edt::Service::objects &selection = (*es)->selection () ;
for (edt::Service::obj_iterator s = selection.begin (); s != selection.end (); ++s) {
for (EditableSelectionIterator s = (*es)->begin_selection (); ! s.at_end (); ++s) {
const lay::CellView &cv = view ()->cellview (s->cv_index ());
db::cell_index_type ci = s->cell_index_tot ();
@ -1219,9 +1221,8 @@ MainService::cm_convert_to_pcell ()
// check whether the selection contains instances and reject it in that case
size_t num_selected = 0;
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
const edt::Service::objects &selection = (*es)->selection ();
num_selected += selection.size ();
for (edt::Service::obj_iterator s = selection.begin (); s != selection.end (); ++s) {
num_selected += (*es)->selection_size ();
for (EditableSelectionIterator s = (*es)->begin_selection (); ! s.at_end (); ++s) {
if (s->is_cell_inst ()) {
throw tl::Exception (tl::to_string (tr ("Selection contains instances - they cannot be converted to PCells.")));
}
@ -1243,8 +1244,7 @@ MainService::cm_convert_to_pcell ()
const db::PCellDeclaration *pc_decl = lib->layout ().pcell_declaration (pc->second);
size_t n = 1000; // 1000 tries max.
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); n > 0 && pc_decl && es != edt_services.end (); ++es) {
const edt::Service::objects &selection = (*es)->selection ();
for (edt::Service::obj_iterator s = selection.begin (); n > 0 && pc_decl && s != selection.end (); ++s) {
for (EditableSelectionIterator s = (*es)->begin_selection (); n > 0 && pc_decl && ! s.at_end (); ++s) {
const lay::CellView &cv = view ()->cellview (s->cv_index ());
if (pc_decl->can_create_from_shape (cv->layout (), s->shape (), s->layer ())) {
--n;
@ -1309,7 +1309,7 @@ MainService::cm_convert_to_pcell ()
manager ()->transaction (tl::to_string (tr ("Convert to PCell")));
}
std::vector<edt::Service::obj_iterator> to_delete;
std::vector<EditableSelectionIterator::pointer> to_delete;
std::vector<lay::ObjectInstPath> new_selection;
bool any_non_converted = false;
@ -1321,8 +1321,7 @@ MainService::cm_convert_to_pcell ()
// convert the shapes which can be converted
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
const edt::Service::objects &selection = (*es)->selection ();
for (edt::Service::obj_iterator s = selection.begin (); s != selection.end (); ++s) {
for (EditableSelectionIterator s = (*es)->begin_selection (); ! s.at_end (); ++s) {
const lay::CellView &cv = view ()->cellview (s->cv_index ());
@ -1346,7 +1345,7 @@ MainService::cm_convert_to_pcell ()
new_selection.back ().add_path (db::InstElement (cell_inst));
// mark the shape for delete (later)
to_delete.push_back (s);
to_delete.push_back (s.operator-> ());
any_converted = true;
@ -1369,7 +1368,7 @@ MainService::cm_convert_to_pcell ()
}
// Delete the shapes which have been converted
for (std::vector<edt::Service::obj_iterator>::const_iterator td = to_delete.begin (); td != to_delete.end (); ++td) {
for (std::vector<EditableSelectionIterator::pointer>::const_iterator td = to_delete.begin (); td != to_delete.end (); ++td) {
db::Cell &cell = view ()->cellview ((*td)->cv_index ())->layout ().cell ((*td)->cell_index ());
if (cell.shapes ((*td)->layer ()).is_valid ((*td)->shape ())) {
cell.shapes ((*td)->layer ()).erase_shape ((*td)->shape ());
@ -1421,8 +1420,7 @@ void MainService::cm_area_perimeter ()
// get (common) cellview index of the primary selection
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
const edt::Service::objects &selection = (*es)->selection ();
for (edt::Service::obj_iterator s = selection.begin (); s != selection.end (); ++s) {
for (EditableSelectionIterator s = (*es)->begin_selection (); ! s.at_end (); ++s) {
if (s->is_cell_inst ()) {
continue;
@ -1528,8 +1526,7 @@ MainService::cm_round_corners ()
// get (common) cellview index of the primary selection
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
const edt::Service::objects &selection = (*es)->selection ();
for (edt::Service::obj_iterator s = selection.begin (); s != selection.end (); ++s) {
for (EditableSelectionIterator s = (*es)->begin_selection (); ! s.at_end (); ++s) {
if (! s->is_cell_inst () && (s->shape ().is_polygon () || s->shape ().is_path () || s->shape ().is_box ())) {
@ -1603,8 +1600,7 @@ MainService::cm_round_corners ()
// Delete the current selection
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
const edt::Service::objects &selection = (*es)->selection ();
for (edt::Service::obj_iterator s = selection.begin (); s != selection.end (); ++s) {
for (EditableSelectionIterator s = (*es)->begin_selection (); ! s.at_end (); ++s) {
if (! s->is_cell_inst () && (s->shape ().is_polygon () || s->shape ().is_path () || s->shape ().is_box ())) {
db::Cell &cell = view ()->cellview (s->cv_index ())->layout ().cell (s->cell_index ());
if (cell.shapes (s->layer ()).is_valid (s->shape ())) {
@ -1664,8 +1660,7 @@ MainService::cm_size ()
// get (common) cellview index of the primary selection
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
const edt::Service::objects &selection = (*es)->selection ();
for (edt::Service::obj_iterator s = selection.begin (); s != selection.end (); ++s) {
for (EditableSelectionIterator s = (*es)->begin_selection (); ! s.at_end (); ++s) {
if (! s->is_cell_inst () && (s->shape ().is_polygon () || s->shape ().is_path () || s->shape ().is_box ())) {
@ -1735,8 +1730,7 @@ MainService::cm_size ()
// Delete the current selection
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
const edt::Service::objects &selection = (*es)->selection ();
for (edt::Service::obj_iterator s = selection.begin (); s != selection.end (); ++s) {
for (EditableSelectionIterator s = (*es)->begin_selection (); ! s.at_end (); ++s) {
if (! s->is_cell_inst () && (s->shape ().is_polygon () || s->shape ().is_path () || s->shape ().is_box ())) {
db::Cell &cell = view ()->cellview (s->cv_index ())->layout ().cell (s->cell_index ());
if (cell.shapes (s->layer ()).is_valid (s->shape ())) {
@ -1792,8 +1786,7 @@ MainService::boolean_op (int mode)
// get (common) cellview index of the primary selection
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
const edt::Service::objects &selection = (*es)->selection ();
for (edt::Service::obj_iterator s = selection.begin (); s != selection.end (); ++s) {
for (EditableSelectionIterator s = (*es)->begin_selection (); ! s.at_end (); ++s) {
if (s->seq () == 0 && ! s->is_cell_inst () && (s->shape ().is_polygon () || s->shape ().is_path () || s->shape ().is_box ())) {
@ -1827,8 +1820,7 @@ MainService::boolean_op (int mode)
// get the secondary selection
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
const edt::Service::objects &selection = (*es)->selection ();
for (edt::Service::obj_iterator s = selection.begin (); s != selection.end (); ++s) {
for (EditableSelectionIterator s = (*es)->begin_selection (); ! s.at_end (); ++s) {
if (s->seq () > 0 && ! s->is_cell_inst () && (s->shape ().is_polygon () || s->shape ().is_path () || s->shape ().is_box ())) {
@ -1865,8 +1857,7 @@ MainService::boolean_op (int mode)
// Let's see whether this heuristics is more accepted.
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
const edt::Service::objects &selection = (*es)->selection ();
for (edt::Service::obj_iterator s = selection.begin (); s != selection.end (); ++s) {
for (EditableSelectionIterator s = (*es)->begin_selection (); ! s.at_end (); ++s) {
if (! s->is_cell_inst () && int (s->layer ()) == layer_index && (s->shape ().is_polygon () || s->shape ().is_path () || s->shape ().is_box ())) {
db::Cell &cell = view ()->cellview (s->cv_index ())->layout ().cell (s->cell_index ());
if (cell.shapes (s->layer ()).is_valid (s->shape ())) {
@ -2001,8 +1992,7 @@ MainService::cm_align ()
// get (common) bbox index of the primary selection
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
const edt::Service::objects &selection = (*es)->selection ();
for (edt::Service::obj_iterator s = selection.begin (); s != selection.end (); ++s) {
for (EditableSelectionIterator s = (*es)->begin_selection (); ! s.at_end (); ++s) {
if (s->seq () == 0) {
@ -2032,13 +2022,11 @@ MainService::cm_align ()
// do the alignment
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
const edt::Service::objects &selection = (*es)->selection ();
// create a transformation vector that describes each shape's transformation
std::vector <db::DCplxTrans> tv;
tv.reserve (selection.size ());
tv.reserve ((*es)->selection_size ());
for (edt::Service::obj_iterator s = selection.begin (); s != selection.end (); ++s) {
for (EditableSelectionIterator s = (*es)->begin_selection (); ! s.at_end (); ++s) {
db::DVector v;
@ -2105,8 +2093,7 @@ MainService::cm_distribute ()
// count the items
size_t n = 0;
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
const edt::Service::objects &selection = (*es)->selection ();
for (edt::Service::obj_iterator s = selection.begin (); s != selection.end (); ++s) {
for (EditableSelectionIterator s = (*es)->begin_selection (); ! s.at_end (); ++s) {
++n;
}
}
@ -2128,8 +2115,7 @@ MainService::cm_distribute ()
objects_for_service.push_back (std::make_pair (i, i));
const edt::Service::objects &selection = (*es)->selection ();
for (edt::Service::obj_iterator s = selection.begin (); s != selection.end (); ++s) {
for (EditableSelectionIterator s = (*es)->begin_selection (); ! s.at_end (); ++s) {
const db::Layout &layout = view ()->cellview (s->cv_index ())->layout ();
db::CplxTrans tr = db::CplxTrans (layout.dbu ()) * s->trans ();
@ -2211,8 +2197,7 @@ MainService::cm_make_array ()
std::vector<edt::Service *> edt_services = view ()->get_plugins <edt::Service> ();
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
const edt::Service::objects &selection = (*es)->selection ();
for (edt::Service::obj_iterator s = selection.begin (); s != selection.end (); ++s) {
for (EditableSelectionIterator s = (*es)->begin_selection (); ! s.at_end (); ++s) {
++n;
}
}
@ -2246,8 +2231,7 @@ MainService::cm_make_array ()
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
const edt::Service::objects &selection = (*es)->selection ();
for (edt::Service::obj_iterator s = selection.begin (); s != selection.end (); ++s) {
for (EditableSelectionIterator s = (*es)->begin_selection (); ! s.at_end (); ++s) {
const lay::CellView &cv = view ()->cellview (s->cv_index ());
if (! cv.is_valid ()) {
@ -2544,8 +2528,7 @@ MainService::check_no_guiding_shapes ()
{
std::vector<edt::Service *> edt_services = view ()->get_plugins <edt::Service> ();
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
const edt::Service::objects &selection = (*es)->selection ();
for (edt::Service::obj_iterator s = selection.begin (); s != selection.end (); ++s) {
for (EditableSelectionIterator s = (*es)->begin_selection (); ! s.at_end (); ++s) {
if (! s->is_cell_inst ()) {
if (s->layer () == view ()->cellview (s->cv_index ())->layout ().guiding_shape_layer ()) {
throw tl::Exception (tl::to_string (tr ("This function cannot be applied to PCell guiding shapes")));

View File

@ -1182,6 +1182,19 @@ PartialService::timeout ()
mp_view->clear_transient_selection ();
clear_mouse_cursors ();
double le = catch_distance () * 3.0; // see Editables::selection_catch_bbox()
db::DBox sel_catch_box = selection_bbox ().enlarged (db::DVector (le, le));
if (has_selection () && sel_catch_box.contains (m_hover_point)) {
// no transient selection if inside current selection - if we click there, we catch the
// currently selected objects
resize_markers (0, true);
resize_inst_markers (0, true);
return;
}
// compute search box
double l = catch_distance ();
db::DBox search_box = db::DBox (m_hover_point, m_hover_point).enlarged (db::DVector (l, l));
@ -2035,45 +2048,13 @@ PartialService::mouse_click_event (const db::DPoint &p, unsigned int buttons, bo
// select is allowed to throw an exception
try {
// compute search box
double l = catch_distance ();
db::DBox search_box = db::DBox (p, p).enlarged (db::DVector (l, l));
// check, if there is a selected shape under the mouse - in this case, we do not do a new selection
PartialShapeFinder finder (true /*point mode*/, m_top_level_sel, db::ShapeIterator::All);
finder.find (view (), search_box);
// check, if there is a selected shape under the mouse - in this case, we do not do a new selection
lay::InstFinder inst_finder (true /*point mode*/, m_top_level_sel, true /*full arrays*/, true /*enclose*/, 0 /*no excludes*/, true /*visible layers*/);
inst_finder.find (view (), search_box);
// collect the founds from the finder
// consider a new selection if new objects are selected or the current selection is shape-only
// (this may happen if points have been inserted)
bool new_selection = ((finder.begin () == finder.end () && inst_finder.begin () == inst_finder.end ())
|| mode != lay::Editable::Replace);
for (PartialShapeFinder::iterator f = finder.begin (); f != finder.end () && ! new_selection; ++f) {
partial_objects::const_iterator sel = m_selection.find (f->first);
new_selection = true;
if (sel != m_selection.end ()) {
for (std::vector<edt::EdgeWithIndex>::const_iterator e = f->second.begin (); e != f->second.end () && new_selection; ++e) {
if (sel->second.find (*e) != sel->second.end ()) {
new_selection = false;
}
}
}
}
if (finder.begin () == finder.end ()) {
for (lay::InstFinder::iterator f = inst_finder.begin (); f != inst_finder.end () && ! new_selection; ++f) {
partial_objects::const_iterator sel = m_selection.find (*f);
if (sel == m_selection.end ()) {
new_selection = true;
}
}
bool new_selection = true;
double le = catch_distance () * 3.0; // see Editables::selection_catch_bbox()
db::DBox sel_catch_box = selection_bbox ().enlarged (db::DVector (le, le));
if (mode == lay::Editable::Replace && has_selection () && sel_catch_box.contains (p)) {
// drag current selection
new_selection = false;
}
if (new_selection) {
@ -2082,6 +2063,18 @@ PartialService::mouse_click_event (const db::DPoint &p, unsigned int buttons, bo
m_selection.clear ();
}
// compute search box
double l = catch_distance ();
db::DBox search_box = db::DBox (p, p).enlarged (db::DVector (l, l));
// identify the edges under the mouse
PartialShapeFinder finder (true /*point mode*/, m_top_level_sel, db::ShapeIterator::All);
finder.find (view (), search_box);
// identify the instances under the mouse
lay::InstFinder inst_finder (true /*point mode*/, m_top_level_sel, true /*full arrays*/, true /*enclose*/, 0 /*no excludes*/, true /*visible layers*/);
inst_finder.find (view (), search_box);
// clear the selection if we now select a guiding shape or if it was consisting of a guiding shape before
// (that way we ensure there is only a guiding shape selected)
PartialShapeFinder::iterator f0 = finder.begin ();

View File

@ -47,10 +47,9 @@ ShapePropertiesPage::ShapePropertiesPage (const std::string &description, edt::S
: lay::PropertiesPage (parent, manager, service),
m_description (description), mp_service (service), m_enable_cb_callback (true)
{
const edt::Service::objects &selection = service->selection ();
m_selection_ptrs.reserve (selection.size ());
for (edt::Service::obj_iterator s = selection.begin (); s != selection.end (); ++s) {
m_selection_ptrs.push_back (s);
m_selection_ptrs.reserve (service->selection_size ());
for (edt::EditableSelectionIterator s = service->begin_selection (); ! s.at_end (); ++s) {
m_selection_ptrs.push_back (s.operator-> ());
}
m_prop_id = 0;
mp_service->clear_highlights ();
@ -191,7 +190,7 @@ ShapePropertiesPage::update ()
{
std::set<const lay::ObjectInstPath *> highlights;
for (auto i = m_indexes.begin (); i != m_indexes.end (); ++i) {
highlights.insert (m_selection_ptrs [*i].operator-> ());
highlights.insert (m_selection_ptrs [*i]);
}
mp_service->highlight (highlights);
@ -201,16 +200,15 @@ ShapePropertiesPage::update ()
void
ShapePropertiesPage::recompute_selection_ptrs (const std::vector<lay::ObjectInstPath> &new_sel)
{
std::map<lay::ObjectInstPath, edt::Service::obj_iterator> ptrs;
std::map<lay::ObjectInstPath, EditableSelectionIterator::pointer> ptrs;
const edt::Service::objects &selection = mp_service->selection ();
for (edt::Service::obj_iterator pos = selection.begin (); pos != selection.end (); ++pos) {
ptrs.insert (std::make_pair (*pos, pos));
for (EditableSelectionIterator pos = mp_service->begin_selection (); ! pos.at_end (); ++pos) {
ptrs.insert (std::make_pair (*pos, pos.operator-> ()));
}
m_selection_ptrs.clear ();
for (std::vector<lay::ObjectInstPath>::const_iterator s = new_sel.begin (); s != new_sel.end (); ++s) {
std::map<lay::ObjectInstPath, edt::Service::obj_iterator>::const_iterator pm = ptrs.find (*s);
std::map<lay::ObjectInstPath, EditableSelectionIterator::pointer>::const_iterator pm = ptrs.find (*s);
tl_assert (pm != ptrs.end ());
m_selection_ptrs.push_back (pm->second);
}
@ -228,7 +226,7 @@ ShapePropertiesPage::do_apply (bool current_only, bool relative)
unsigned int cv_index = m_selection_ptrs [m_indexes.front ()]->cv_index ();
{
edt::Service::obj_iterator pos = m_selection_ptrs [m_indexes.front ()];
EditableSelectionIterator::pointer pos = m_selection_ptrs [m_indexes.front ()];
tl_assert (! pos->is_cell_inst ());
const lay::CellView &cv = mp_service->view ()->cellview (pos->cv_index ());
@ -261,7 +259,7 @@ ShapePropertiesPage::do_apply (bool current_only, bool relative)
std::vector<lay::ObjectInstPath> new_sel;
new_sel.reserve (m_selection_ptrs.size ());
for (std::vector<edt::Service::obj_iterator>::const_iterator p = m_selection_ptrs.begin (); p != m_selection_ptrs.end (); ++p) {
for (std::vector<EditableSelectionIterator::pointer>::const_iterator p = m_selection_ptrs.begin (); p != m_selection_ptrs.end (); ++p) {
new_sel.push_back (**p);
}
@ -274,7 +272,7 @@ ShapePropertiesPage::do_apply (bool current_only, bool relative)
for (auto i = m_indexes.begin (); i != m_indexes.end (); ++i) {
size_t index = *i;
edt::Service::obj_iterator pos = m_selection_ptrs [*i];
EditableSelectionIterator::pointer pos = m_selection_ptrs [*i];
// only update objects from the same layout - this is not practical limitation but saves a lot of effort for
// managing different property id's etc.
@ -376,7 +374,7 @@ ShapePropertiesPage::update_shape ()
return;
}
edt::Service::obj_iterator pos = m_selection_ptrs [m_indexes.front ()];
EditableSelectionIterator::pointer pos = m_selection_ptrs [m_indexes.front ()];
const lay::CellView &cv = mp_service->view ()->cellview (pos->cv_index ());
double dbu = cv->layout ().dbu ();

View File

@ -71,7 +71,7 @@ private:
protected:
std::string m_description;
std::vector<edt::Service::obj_iterator> m_selection_ptrs;
std::vector<EditableSelectionIterator::pointer> m_selection_ptrs;
std::vector<size_t> m_indexes;
edt::Service *mp_service;
bool m_enable_cb_callback;

View File

@ -459,11 +459,10 @@ Service::copy_selected ()
unsigned int inst_mode = 0;
if (m_hier_copy_mode < 0) {
const objects &sel = selection ();
for (objects::const_iterator r = sel.begin (); r != sel.end () && ! need_to_ask_for_copy_mode; ++r) {
for (EditableSelectionIterator r = begin_selection (); ! r.at_end () && ! need_to_ask_for_copy_mode; ++r) {
if (r->is_cell_inst ()) {
const db::Cell &cell = view ()->cellview (r->cv_index ())->layout ().cell (r->back ().inst_ptr.cell_index ());
if (! cell.is_proxy ()) {
if (! cell.is_proxy () && ! cell.is_leaf ()) {
need_to_ask_for_copy_mode = true;
}
}
@ -500,12 +499,10 @@ Service::copy_selected ()
void
Service::copy_selected (unsigned int inst_mode)
{
const objects &sel = selection ();
// create one ClipboardData object per cv_index because, this one assumes that there is
// only one source layout object.
std::set <unsigned int> cv_indices;
for (objects::const_iterator r = sel.begin (); r != sel.end (); ++r) {
for (EditableSelectionIterator r = begin_selection (); ! r.at_end (); ++r) {
cv_indices.insert (r->cv_index ());
}
@ -515,7 +512,7 @@ Service::copy_selected (unsigned int inst_mode)
// add the selected objects to the clipboard data objects.
const lay::CellView &cv = view ()->cellview (*cvi);
for (objects::const_iterator r = sel.begin (); r != sel.end (); ++r) {
for (EditableSelectionIterator r = begin_selection (); ! r.at_end (); ++r) {
if (r->cv_index () == *cvi) {
if (! r->is_cell_inst ()) {
cd->get ().add (cv->layout (), r->layer (), r->shape (), cv.context_trans () * r->trans ());
@ -618,8 +615,7 @@ Service::selection_bbox ()
db::DBox box;
const objects &sel = selection ();
for (objects::const_iterator r = sel.begin (); r != sel.end (); ++r) {
for (EditableSelectionIterator r = begin_selection (); ! r.at_end (); ++r) {
const lay::CellView &cv = view ()->cellview (r->cv_index ());
const db::Layout &layout = cv->layout ();
@ -699,13 +695,10 @@ Service::transform (const db::DCplxTrans &trans, const std::vector<db::DCplxTran
size_t n;
const objects &sel = selection ();
// build a list of object references corresponding to the p_trv vector
std::vector <objects::iterator> obj_ptrs;
obj_ptrs.reserve (sel.size ());
for (objects::iterator r = sel.begin (); r != sel.end (); ++r) {
obj_ptrs.push_back (r);
std::vector <EditableSelectionIterator::pointer> obj_ptrs;
for (EditableSelectionIterator r = begin_selection (); ! r.at_end (); ++r) {
obj_ptrs.push_back (r.operator-> ());
}
// build the transformation variants cache
@ -717,7 +710,7 @@ Service::transform (const db::DCplxTrans &trans, const std::vector<db::DCplxTran
// The key is a triple: cell_index, cv_index, layer
std::map <std::pair <db::cell_index_type, std::pair <unsigned int, unsigned int> >, std::vector <size_t> > shapes_by_cell;
n = 0;
for (objects::iterator r = sel.begin (); r != sel.end (); ++r, ++n) {
for (EditableSelectionIterator r = begin_selection (); ! r.at_end (); ++r, ++n) {
if (! r->is_cell_inst ()) {
shapes_by_cell.insert (std::make_pair (std::make_pair (r->cell_index (), std::make_pair (r->cv_index (), r->layer ())), std::vector <size_t> ())).first->second.push_back (n);
}
@ -740,7 +733,7 @@ Service::transform (const db::DCplxTrans &trans, const std::vector<db::DCplxTran
for (std::vector <size_t>::iterator si = sbc->second.begin (); si != sbc->second.end (); ++si) {
objects::iterator s = obj_ptrs [*si];
EditableSelectionIterator::pointer s = obj_ptrs [*si];
// mt = transformation in DBU units
db::ICplxTrans mt;
@ -764,14 +757,14 @@ Service::transform (const db::DCplxTrans &trans, const std::vector<db::DCplxTran
for (std::vector <size_t>::iterator si = sbc->second.begin (); si != sbc->second.end (); ++si) {
objects::iterator &s = obj_ptrs [*si];
EditableSelectionIterator::pointer &s = obj_ptrs [*si];
lay::ObjectInstPath new_path (*s);
new_path.set_shape (new_shapes.find (s->shape ())->second);
// modify the selection
m_selection.erase (s);
s = m_selection.insert (new_path).first;
m_selection.erase (*s);
s = m_selection.insert (new_path).first.operator-> ();
}
@ -787,7 +780,7 @@ Service::transform (const db::DCplxTrans &trans, const std::vector<db::DCplxTran
// The key is a pair: cell_index, cv_index
std::map <std::pair <db::cell_index_type, unsigned int>, std::vector <size_t> > insts_by_cell;
n = 0;
for (objects::iterator r = sel.begin (); r != sel.end (); ++r, ++n) {
for (EditableSelectionIterator r = begin_selection (); ! r.at_end (); ++r, ++n) {
if (r->is_cell_inst ()) {
insts_by_cell.insert (std::make_pair (std::make_pair (r->cell_index (), r->cv_index ()), std::vector <size_t> ())).first->second.push_back (n);
}
@ -810,7 +803,7 @@ Service::transform (const db::DCplxTrans &trans, const std::vector<db::DCplxTran
for (std::vector <size_t>::iterator ii = ibc->second.begin (); ii != ibc->second.end (); ++ii) {
objects::iterator i = obj_ptrs [*ii];
EditableSelectionIterator::pointer i = obj_ptrs [*ii];
// mt = transformation in DBU units
db::ICplxTrans mt;
@ -836,14 +829,14 @@ Service::transform (const db::DCplxTrans &trans, const std::vector<db::DCplxTran
for (std::vector <size_t>::iterator ii = ibc->second.begin (); ii != ibc->second.end (); ++ii) {
objects::iterator &i = obj_ptrs [*ii];
EditableSelectionIterator::pointer &i = obj_ptrs [*ii];
lay::ObjectInstPath new_path (*i);
new_path.back ().inst_ptr = new_insts.find (i->back ().inst_ptr)->second;
// modify the selection
m_selection.erase (i);
i = m_selection.insert (new_path).first;
m_selection.erase (*i);
i = m_selection.insert (new_path).first.operator-> ();
}
@ -1039,8 +1032,7 @@ Service::del_selected ()
std::set<db::Layout *> needs_cleanup;
// delete all shapes and instances.
const objects &sel = selection ();
for (objects::const_iterator r = sel.begin (); r != sel.end (); ++r) {
for (EditableSelectionIterator r = begin_selection (); ! r.at_end (); ++r) {
const lay::CellView &cv = view ()->cellview (r->cv_index ());
if (cv.is_valid ()) {
db::Cell &cell = cv->layout ().cell (r->cell_index ());
@ -1326,11 +1318,12 @@ static std::string path_to_string (const db::Layout &layout, const lay::ObjectIn
void
Service::display_status (bool transient)
{
const objects *sel = transient ? &transient_selection () : &selection ();
EditableSelectionIterator r = transient ? begin_transient_selection () : begin_selection ();
EditableSelectionIterator rr = r;
++rr;
if (sel->size () == 1) {
if (rr.at_end ()) {
objects::const_iterator r = sel->begin ();
const db::Layout &layout = view ()->cellview (r->cv_index ())->layout ();
if (m_cell_inst_service) {
@ -1606,6 +1599,18 @@ Service::transient_selection () const
return m_transient_selection;
}
EditableSelectionIterator
Service::begin_selection () const
{
return EditableSelectionIterator (this, false);
}
EditableSelectionIterator
Service::begin_transient_selection () const
{
return EditableSelectionIterator (this, true);
}
bool
Service::select (const lay::ObjectInstPath &obj, lay::Editable::SelectionMode mode)
{
@ -1730,17 +1735,15 @@ Service::selection_to_view ()
void
Service::do_selection_to_view ()
{
const objects &sel = selection ();
// Hint: this is a lower bound:
m_markers.reserve (sel.size ());
m_markers.reserve (selection_size ());
// build the transformation variants cache
TransformationVariants tv (view ());
// Build markers
for (objects::const_iterator r = sel.begin (); r != sel.end (); ++r) {
for (EditableSelectionIterator r = begin_selection (); ! r.at_end (); ++r) {
const lay::CellView &cv = view ()->cellview (r->cv_index ());
@ -1939,12 +1942,14 @@ Service::handle_guiding_shape_changes (const lay::ObjectInstPath &obj) const
bool
Service::handle_guiding_shape_changes ()
{
EditableSelectionIterator s = begin_selection ();
// just allow one guiding shape to be selected
if (selection ().empty ()) {
if (s.at_end ()) {
return false;
}
std::pair<bool, lay::ObjectInstPath> gs = handle_guiding_shape_changes (*selection ().begin ());
std::pair<bool, lay::ObjectInstPath> gs = handle_guiding_shape_changes (*s);
if (gs.first) {
// remove superfluous proxies
@ -1966,7 +1971,20 @@ Service::handle_guiding_shape_changes ()
// Implementation of EditableSelectionIterator
EditableSelectionIterator::EditableSelectionIterator (const std::vector<edt::Service *> &services, bool transient)
: m_services (services), m_service (0), m_transient_selection (transient)
: m_services (services.begin (), services.end ()), m_service (0), m_transient_selection (transient)
{
init ();
}
EditableSelectionIterator::EditableSelectionIterator (const edt::Service *service, bool transient)
: m_services (), m_service (0), m_transient_selection (transient)
{
m_services.push_back (service);
init ();
}
void
EditableSelectionIterator::init ()
{
if (! m_services.empty ()) {
if (m_transient_selection) {

View File

@ -71,6 +71,41 @@ std::map<std::string, tl::Variant> pcell_parameters_from_string (const std::stri
// -------------------------------------------------------------
/**
* @brief A utility class to implement a selection iterator across all editor services
*/
class EDT_PUBLIC EditableSelectionIterator
{
public:
typedef std::set<lay::ObjectInstPath> objects;
typedef objects::value_type value_type;
typedef objects::const_iterator iterator_type;
typedef const value_type *pointer;
typedef const value_type &reference;
typedef std::forward_iterator_tag iterator_category;
typedef void difference_type;
EditableSelectionIterator (const std::vector<edt::Service *> &services, bool transient);
EditableSelectionIterator (const edt::Service *service, bool transient);
bool at_end () const;
EditableSelectionIterator &operator++ ();
reference operator* () const;
pointer operator-> () const;
private:
std::vector<const edt::Service *> m_services;
unsigned int m_service;
bool m_transient_selection;
iterator_type m_iter, m_end;
void next ();
void init ();
};
// -------------------------------------------------------------
class EDT_PUBLIC Service
: public lay::EditorServiceBase,
public db::Object
@ -226,16 +261,6 @@ public:
return m_color;
}
/**
* @brief Get the selection container
*/
const objects &selection () const;
/**
* @brief Get the transient selection container
*/
const objects &transient_selection () const;
/**
* @brief Access to the view object
*/
@ -257,11 +282,21 @@ public:
*/
void clear_previous_selection ();
/**
* @brief Gets the selection iterator
*/
EditableSelectionIterator begin_selection () const;
/**
* @brief Establish a transient selection
*/
bool transient_select (const db::DPoint &pos);
/**
* @brief Gets the transient selection iterator
*/
EditableSelectionIterator begin_transient_selection () const;
/**
* @brief Turns the transient selection to the selection
*/
@ -585,6 +620,8 @@ protected:
lay::PointSnapToObjectResult snap2_details (const db::DPoint &p) const;
private:
friend class EditableSelectionIterator;
// The layout view that the editor service is attached to
lay::LayoutViewBase *mp_view;
@ -685,6 +722,16 @@ private:
*/
void apply_highlights ();
/**
* @brief Get the selection container
*/
const objects &selection () const;
/**
* @brief Get the transient selection container
*/
const objects &transient_selection () const;
private:
void copy_selected (unsigned int inst_mode);
void update_vector_snapped_point (const db::DPoint &pt, db::DVector &vr, bool &result_set) const;
@ -692,36 +739,6 @@ private:
void update_vector_snapped_marker (const lay::InstanceMarker *sm, const db::DTrans &trans, db::DVector &vr, bool &result_set, size_t &count) const;
};
/**
* @brief A utility class to implement a selection iterator across all editor services
*/
class EditableSelectionIterator
{
public:
typedef edt::Service::objects::value_type value_type;
typedef edt::Service::objects::const_iterator iterator_type;
typedef const value_type *pointer;
typedef const value_type &reference;
typedef std::forward_iterator_tag iterator_category;
typedef void difference_type;
EditableSelectionIterator (const std::vector<edt::Service *> &services, bool transient);
bool at_end () const;
EditableSelectionIterator &operator++ ();
reference operator* () const;
pointer operator-> () const;
private:
std::vector<edt::Service *> m_services;
unsigned int m_service;
bool m_transient_selection;
iterator_type m_iter, m_end;
void next ();
};
/**
* @brief Gets the combined selections over all editor services in the layout view
*/

View File

@ -94,16 +94,23 @@ namespace lay
// --------------------------------------------------------------------------------
// Exception handlers
static void close_transaction ()
{
// if any transaction is pending (this may happen when an operation threw an exception)
// close transactions.
// NOTE: don't do this in breakpoint mode as we do not want to interfere with things happening outside
if (lay::MainWindow::instance () && lay::MainWindow::instance ()->manager ().transacting () &&
!(lay::MacroEditorDialog::instance () && lay::MacroEditorDialog::instance ()->in_breakpoint ())) {
lay::MainWindow::instance ()->manager ().commit ();
}
}
static void ui_exception_handler_tl (const tl::Exception &ex, QWidget *parent)
{
// Prevents severe side effects if there are pending deferred methods
tl::NoDeferredMethods silent;
// if any transaction is pending (this may happen when an operation threw an exception)
// close transactions.
if (lay::MainWindow::instance () && lay::MainWindow::instance ()->manager ().transacting ()) {
lay::MainWindow::instance ()->manager ().commit ();
}
close_transaction ();
const tl::ExitException *gsi_exit = dynamic_cast <const tl::ExitException *> (&ex);
const tl::BreakException *gsi_break = dynamic_cast <const tl::BreakException *> (&ex);
@ -155,13 +162,9 @@ static void ui_exception_handler_std (const std::exception &ex, QWidget *parent)
// Prevents severe side effects if there are pending deferred methods
tl::NoDeferredMethods silent;
// if any transaction is pending (this may happen when an operation threw an exception)
// close transactions.
if (lay::MainWindow::instance () && lay::MainWindow::instance ()->manager ().transacting ()) {
lay::MainWindow::instance ()->manager ().commit ();
}
close_transaction ();
tl::error << ex.what ();
tl::error << ex.what ();
if (! parent) {
parent = QApplication::activeWindow () ? QApplication::activeWindow () : lay::MainWindow::instance ();
}
@ -173,11 +176,7 @@ static void ui_exception_handler_def (QWidget *parent)
// Prevents severe side effects if there are pending deferred methods
tl::NoDeferredMethods silent;
// if any transaction is pending (this may happen when an operation threw an exception)
// close transactions.
if (lay::MainWindow::instance () && lay::MainWindow::instance ()->manager ().transacting ()) {
lay::MainWindow::instance ()->manager ().commit ();
}
close_transaction ();
if (! parent) {
parent = QApplication::activeWindow () ? QApplication::activeWindow () : lay::MainWindow::instance ();

View File

@ -408,7 +408,7 @@ FillDialog::get_fill_parameters ()
// selection
std::vector<edt::Service *> edt_services = mp_view->get_plugins <edt::Service> ();
for (std::vector<edt::Service *>::const_iterator s = edt_services.begin (); s != edt_services.end (); ++s) {
for (edt::Service::objects::const_iterator sel = (*s)->selection ().begin (); sel != (*s)->selection ().end (); ++sel) {
for (edt::EditableSelectionIterator sel = (*s)->begin_selection (); ! sel.at_end (); ++sel) {
if (! sel->is_cell_inst () && (sel->shape ().is_polygon () || sel->shape ().is_path () || sel->shape ().is_box ())) {
db::Polygon poly;
sel->shape ().polygon (poly);

View File

@ -1511,26 +1511,25 @@ MacroEditorDialog::eventFilter (QObject *obj, QEvent *event)
if (lay::BusySection::is_busy ()) {
#if 0
if (dynamic_cast <QInputEvent *> (event) != 0 || dynamic_cast <QPaintEvent *> (event) != 0) {
if (m_in_breakpoint && (dynamic_cast <QInputEvent *> (event) != 0 || dynamic_cast <QPaintEvent *> (event) != 0)) {
// In breakpoint or execution mode and while processing the events inside the debugger,
// In breakpoint mode and while processing the events inside the debugger,
// ignore all input or paint events targeted to widgets which are not children of this or the assistant dialog.
// Ignoring the paint event is required because otherwise a repaint action would be triggered on a layout which
// is potentially unstable or inconsistent.
// We nevertheless allow events send to a HelpDialog or ProgressWidget since those are vital for the application's
// functionality and are known not to cause any interference.
QObject *rec = obj;
while (rec && (rec != this && !dynamic_cast<lay::HelpDialog *> (rec) && !dynamic_cast<lay::ProgressWidget *> (rec))) {
while (rec && rec != this) {
rec = rec->parent ();
}
if (! rec) {
// TODO: reschedule the paint events (?)
event->accept ();
return true;
}
}
#endif
} else {
@ -2267,19 +2266,35 @@ END_PROTECTED
}
void
MacroEditorDialog::help_requested(const QString &s)
MacroEditorDialog::help_requested (const QString &s)
{
BEGIN_PROTECTED
// Do not allow modal popups in breakpoint mode - this would interfere with
// event filtering during breakpoint execution
if (m_in_breakpoint) {
throw tl::Exception (tl::to_string (tr ("The help function is not available in breakpoint mode.")));
}
lay::MainWindow::instance ()->show_assistant_topic (tl::to_string (s));
END_PROTECTED
}
void
MacroEditorDialog::help_button_clicked()
MacroEditorDialog::help_button_clicked ()
{
BEGIN_PROTECTED
// Do not allow modal popups in breakpoint mode - this would interfere with
// event filtering during breakpoint execution
if (m_in_breakpoint) {
throw tl::Exception (tl::to_string (tr ("The help function is not available in breakpoint mode.")));
}
lay::MainWindow::instance ()->show_assistant_url ("int:/code/index.xml");
END_PROTECTED
}
void
MacroEditorDialog::add_button_clicked()
MacroEditorDialog::add_button_clicked ()
{
BEGIN_PROTECTED
new_macro ();
@ -2287,7 +2302,7 @@ END_PROTECTED
}
lym::Macro *
MacroEditorDialog::new_macro()
MacroEditorDialog::new_macro ()
{
ensure_writeable_collection_selected ();
@ -3451,6 +3466,12 @@ MacroEditorDialog::leave_breakpoint_mode ()
mp_current_interpreter = 0;
do_update_ui_to_run_mode ();
set_exec_point (0, -1, -1);
// refresh UI that might have been spoiled because we filter events
auto tl_widgets = QApplication::topLevelWidgets ();
for (auto w = tl_widgets.begin (); w != tl_widgets.end (); ++w) {
(*w)->update ();
}
}
void

View File

@ -166,6 +166,14 @@ public:
return m_in_exec;
}
/**
* @brief Returns true while the macro IDE is in breakpoint mode
*/
bool in_breakpoint () const
{
return m_in_breakpoint;
}
/**
* @brief Selects the current category in the tree view
*/

View File

@ -122,6 +122,7 @@ public:
options.push_back (std::pair<std::string, std::string> (cfg_line_style_palette, lay::LineStylePalette ().to_string ()));
options.push_back (std::pair<std::string, std::string> (cfg_no_stipple, "false"));
options.push_back (std::pair<std::string, std::string> (cfg_markers_visible, "true"));
options.push_back (std::pair<std::string, std::string> (cfg_copy_cell_mode, "-1"));
}
};

View File

@ -132,6 +132,7 @@ static const std::string cfg_default_font_size ("default-font-size");
static const std::string cfg_hide_empty_layers ("hide-empty-layers");
static const std::string cfg_test_shapes_in_view ("test-shapes-in-view");
static const std::string cfg_copy_cell_mode ("copy-cell-mode");
static const std::string cfg_flat_cell_list ("flat-cell-list");
static const std::string cfg_split_cell_list ("split-cell-list");
static const std::string cfg_cell_list_sorting ("cell-list-sorting");

View File

@ -1,46 +1,41 @@
<ui version="4.0" >
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CopyCellModeDialog</class>
<widget class="QDialog" name="CopyCellModeDialog" >
<property name="geometry" >
<widget class="QDialog" name="CopyCellModeDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>178</height>
<width>546</width>
<height>198</height>
</rect>
</property>
<property name="windowTitle" >
<property name="windowTitle">
<string>Copy Cell Options</string>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>9</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox" >
<property name="title" >
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Copy Cell Mode</string>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>9</number>
</property>
<property name="spacing" >
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin" stdset="0">
<number>9</number>
</property>
<item>
<widget class="QRadioButton" name="shallow_rb" >
<property name="text" >
<widget class="QRadioButton" name="shallow_rb">
<property name="text">
<string>Shallow copy (don't copy subcells)</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="deep_rb" >
<property name="text" >
<widget class="QRadioButton" name="deep_rb">
<property name="text">
<string>Deep copy (include subcells)</string>
</property>
</widget>
@ -48,12 +43,19 @@
</layout>
</widget>
</item>
<item>
<widget class="QCheckBox" name="dont_ask_cbx">
<property name="text">
<string>Don't ask again (you can always reset this in Setup: Application/Cells page)</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" >
<property name="sizeHint" stdset="0">
<size>
<width>382</width>
<height>31</height>
@ -62,12 +64,12 @@
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox" >
<property name="orientation" >
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons" >
<set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
@ -86,11 +88,11 @@
<receiver>CopyCellModeDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel" >
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel" >
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
@ -102,11 +104,11 @@
<receiver>CopyCellModeDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel" >
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel" >
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>

View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LayoutViewConfigPage8</class>
<widget class="QWidget" name="LayoutViewConfigPage8">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>414</width>
<height>46</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Cell copy mode</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="hier_copy_mode_cbx">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
<item>
<property name="text">
<string>Shallow mode (cell only)</string>
</property>
</item>
<item>
<property name="text">
<string>Deep mode (cell and subcells)</string>
</property>
</item>
<item>
<property name="text">
<string>Ask</string>
</property>
</item>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -579,7 +579,7 @@ CopyCellModeDialog::~CopyCellModeDialog ()
}
bool
CopyCellModeDialog::exec_dialog (int &copy_mode)
CopyCellModeDialog::exec_dialog (int &copy_mode, bool &dont_ask)
{
QRadioButton *buttons [] = { mp_ui->shallow_rb, mp_ui->deep_rb };
@ -592,6 +592,7 @@ CopyCellModeDialog::exec_dialog (int &copy_mode)
if (buttons [i]->isChecked ()) {
copy_mode = i;
}
dont_ask = mp_ui->dont_ask_cbx->isChecked ();
}
return true;
} else {

View File

@ -233,7 +233,7 @@ public:
*
* The mode is either 0 (for shallow), 1 (for deep)
*/
bool exec_dialog (int &copy_mode);
bool exec_dialog (int &copy_mode, bool &dont_ask_again);
private:
Ui::CopyCellModeDialog *mp_ui;

View File

@ -218,6 +218,7 @@ HierarchyControlPanel::HierarchyControlPanel (lay::LayoutViewBase *view, QWidget
m_flat (false),
m_split_mode (false),
m_sorting (CellTreeModel::ByName),
m_cell_copy_mode (-1),
m_do_update_content_dm (this, &HierarchyControlPanel::do_update_content),
m_do_full_update_content_dm (this, &HierarchyControlPanel::do_full_update_content)
{
@ -398,6 +399,12 @@ HierarchyControlPanel::clear_all ()
mp_cell_lists.clear ();
}
void
HierarchyControlPanel::set_cell_copy_mode (int m)
{
m_cell_copy_mode = m;
}
void
HierarchyControlPanel::set_flat (bool f)
{
@ -1002,6 +1009,47 @@ HierarchyControlPanel::has_focus () const
return m_active_index >= 0 && m_active_index < int (mp_cell_lists.size ()) && mp_cell_lists [m_active_index]->hasFocus ();
}
bool
HierarchyControlPanel::ask_for_cell_copy_mode (const db::Layout &layout, const std::vector<cell_path_type> &paths, int &cell_copy_mode)
{
bool needs_to_ask = false;
cell_copy_mode = 0;
if (m_cell_copy_mode < 0) { // ask
// check if there is a cell that we have to ask for
for (std::vector<cell_path_type>::const_iterator p = paths.begin (); p != paths.end (); ++p) {
if (! p->empty ()) {
const db::Cell &cell = layout.cell (p->back ());
if (! cell.is_proxy () && ! cell.is_leaf ()) {
needs_to_ask = true;
}
}
}
} else {
cell_copy_mode = m_cell_copy_mode;
}
if (needs_to_ask) {
bool dont_ask_again = false;
lay::CopyCellModeDialog mode_dialog (this);
if (! mode_dialog.exec_dialog (cell_copy_mode, dont_ask_again)) {
return false;
}
if (dont_ask_again) {
view ()->dispatcher ()->config_set (cfg_copy_cell_mode, tl::to_string (cell_copy_mode));
view ()->dispatcher ()->config_end ();
}
}
return true;
}
void
HierarchyControlPanel::cut ()
{
@ -1017,34 +1065,25 @@ HierarchyControlPanel::cut ()
}
// first copy
bool needs_to_ask = false;
db::Layout &layout = m_cellviews [m_active_index]->layout ();
if (! layout.is_editable ()) {
return;
}
// collect the called cells of the cells to copy, so we don't copy a cell twice
db::Clipboard::instance ().clear ();
// don't copy the cells which would be copied anyway
int cut_mode = 1; // 0: shallow, 1: deep
if (! ask_for_cell_copy_mode (layout, paths, cut_mode)) {
return;
}
// collect the called cells of the cells to copy, so we don't copy a cell twice
std::set<db::cell_index_type> called_cells;
for (std::vector<cell_path_type>::const_iterator p = paths.begin (); p != paths.end (); ++p) {
if (! p->empty ()) {
const db::Cell &cell = layout.cell (p->back ());
cell.collect_called_cells (called_cells);
if (cell.cell_instances () > 0) {
needs_to_ask = true;
}
}
}
int cut_mode = 1; // 0: shallow, 1: deep
if (needs_to_ask) {
lay::CopyCellModeDialog mode_dialog (this);
if (! mode_dialog.exec_dialog (cut_mode)) {
return;
}
}
@ -1115,34 +1154,25 @@ HierarchyControlPanel::copy ()
return;
}
bool needs_to_ask = false;
db::Layout &layout = m_cellviews [m_active_index]->layout ();
// collect the called cells of the cells to copy, so we don't copy a cell twice
db::Clipboard::instance ().clear ();
// don't copy the cells which would be copied anyway
int copy_mode = 1; // 0: shallow, 1: deep
if (! ask_for_cell_copy_mode (layout, paths, copy_mode)) {
return;
}
// collect the called cells of the cells to copy, so we don't copy a cell twice
std::set<db::cell_index_type> called_cells;
for (std::vector<cell_path_type>::const_iterator p = paths.begin (); p != paths.end (); ++p) {
if (! p->empty ()) {
const db::Cell &cell = layout.cell (p->back ());
cell.collect_called_cells (called_cells);
if (cell.cell_instances () > 0) {
needs_to_ask = true;
}
}
}
int copy_mode = 1; // 0: shallow, 1: deep
if (needs_to_ask) {
lay::CopyCellModeDialog mode_dialog (this);
if (! mode_dialog.exec_dialog (copy_mode)) {
return;
}
}
// actually copy
for (std::vector<cell_path_type>::const_iterator p = paths.begin (); p != paths.end (); ++p) {
if (! p->empty () && called_cells.find (p->back ()) == called_cells.end ()) {
db::ClipboardValue<lay::CellClipboardData> *cd = new db::ClipboardValue<lay::CellClipboardData> ();

View File

@ -219,6 +219,12 @@ public:
*/
void paste ();
/**
* @brief Selects cell copy mode
* 0: shallow, 1: deep, -1: ask
*/
void set_cell_copy_mode (int m);
/**
* @brief Return true, if the panel has a selection
*/
@ -308,6 +314,7 @@ private:
QSplitter *mp_splitter;
tl::Color m_background_color;
tl::Color m_text_color;
int m_cell_copy_mode;
tl::DeferredMethod<HierarchyControlPanel> m_do_update_content_dm;
tl::DeferredMethod<HierarchyControlPanel> m_do_full_update_content_dm;
std::unique_ptr<QStyle> mp_tree_style;
@ -336,6 +343,9 @@ private:
// clears all widgets of the cell lists
void clear_all ();
// ask for cell copy mode
bool ask_for_cell_copy_mode (const db::Layout &layout, const std::vector<cell_path_type> &paths, int &cell_copy_mode);
};
} // namespace lay

View File

@ -42,6 +42,7 @@
#include "ui_LayoutViewConfigPage6.h"
#include "ui_LayoutViewConfigPage6a.h"
#include "ui_LayoutViewConfigPage7.h"
#include "ui_LayoutViewConfigPage8.h"
#include "laySelectStippleForm.h"
#include "laySelectLineStyleForm.h"
@ -1529,6 +1530,37 @@ LayoutViewConfigPage7::commit (lay::Dispatcher *root)
root->config_set (cfg_initial_hier_depth, mp_ui->def_depth->value ());
}
// ------------------------------------------------------------
// LayoutConfigPage8 implementation
LayoutViewConfigPage8::LayoutViewConfigPage8 (QWidget *parent)
: lay::ConfigPage (parent)
{
mp_ui = new Ui::LayoutViewConfigPage8 ();
mp_ui->setupUi (this);
}
LayoutViewConfigPage8::~LayoutViewConfigPage8 ()
{
delete mp_ui;
mp_ui = 0;
}
void
LayoutViewConfigPage8::setup (lay::Dispatcher *root)
{
int cpm = -1;
root->config_get (cfg_copy_cell_mode, cpm);
mp_ui->hier_copy_mode_cbx->setCurrentIndex ((cpm < 0 || cpm > 1) ? 2 : cpm);
}
void
LayoutViewConfigPage8::commit (lay::Dispatcher *root)
{
int cpm = mp_ui->hier_copy_mode_cbx->currentIndex ();
root->config_set (cfg_copy_cell_mode, (cpm < 0 || cpm > 1) ? -1 : cpm);
}
// ------------------------------------------------------------
// The dummy plugin declaration to register the configuration options
@ -1554,6 +1586,7 @@ public:
pages.push_back (std::make_pair (tl::to_string (QObject::tr ("Application|Tracking")), new LayoutViewConfigPage2d (parent)));
pages.push_back (std::make_pair (tl::to_string (QObject::tr ("Application|Layer Properties")), new LayoutViewConfigPage5 (parent)));
pages.push_back (std::make_pair (tl::to_string (QObject::tr ("Application|Units")), new LayoutViewConfigPage3c (parent)));
pages.push_back (std::make_pair (tl::to_string (QObject::tr ("Application|Cells")), new LayoutViewConfigPage8 (parent)));
pages.push_back (std::make_pair (tl::to_string (QObject::tr ("Navigation|New Cell")), new LayoutViewConfigPage3a (parent)));
pages.push_back (std::make_pair (tl::to_string (QObject::tr ("Navigation|Zoom And Pan")), new LayoutViewConfigPage3b (parent)));

View File

@ -50,6 +50,7 @@ namespace Ui {
class LayoutViewConfigPage6;
class LayoutViewConfigPage6a;
class LayoutViewConfigPage7;
class LayoutViewConfigPage8;
}
namespace lay
@ -355,6 +356,22 @@ private:
Ui::LayoutViewConfigPage7 *mp_ui;
};
class LayoutViewConfigPage8
: public lay::ConfigPage
{
Q_OBJECT
public:
LayoutViewConfigPage8 (QWidget *parent);
~LayoutViewConfigPage8 ();
virtual void setup (lay::Dispatcher *root);
virtual void commit (lay::Dispatcher *root);
private:
Ui::LayoutViewConfigPage8 *mp_ui;
};
}
#endif

View File

@ -40,6 +40,7 @@ FORMS = \
LayoutViewConfigPage6.ui \
LayoutViewConfigPage7.ui \
LayoutViewConfigPage.ui \
LayoutViewConfigPage8.ui \
LibraryCellSelectionForm.ui \
LoadLayoutOptionsDialog.ui \
MarkerBrowserConfigPage2.ui \

View File

@ -46,6 +46,7 @@ std::string cfg_rdb_show_all ("rdb-show-all");
std::string cfg_rdb_list_shapes ("rdb-list-shapes");
std::string cfg_rdb_window_state ("rdb-window-state-v2"); // v2: 0.24++
std::string cfg_rdb_window_mode ("rdb-window-mode");
std::string cfg_rdb_tree_state ("rdb-tree-state");
std::string cfg_rdb_window_dim ("rdb-window-dim");
std::string cfg_rdb_max_marker_count ("rdb-max-marker-count");
std::string cfg_rdb_marker_color ("rdb-marker-color");
@ -296,6 +297,7 @@ public:
options.push_back (std::pair<std::string, std::string> (cfg_rdb_window_mode, "fit-marker"));
options.push_back (std::pair<std::string, std::string> (cfg_rdb_window_state, ""));
options.push_back (std::pair<std::string, std::string> (cfg_rdb_window_dim, "1.0"));
options.push_back (std::pair<std::string, std::string> (cfg_rdb_tree_state, ""));
options.push_back (std::pair<std::string, std::string> (cfg_rdb_max_marker_count, "1000"));
options.push_back (std::pair<std::string, std::string> (cfg_rdb_marker_color, lay::ColorConverter ().to_string (QColor ())));
options.push_back (std::pair<std::string, std::string> (cfg_rdb_marker_line_width, "-1"));

View File

@ -55,6 +55,7 @@ extern std::string cfg_rdb_list_shapes;
extern std::string cfg_rdb_window_state;
extern std::string cfg_rdb_window_mode;
extern std::string cfg_rdb_window_dim;
extern std::string cfg_rdb_tree_state;
extern std::string cfg_rdb_max_marker_count;
extern std::string cfg_rdb_marker_color;
extern std::string cfg_rdb_marker_line_width;
@ -847,6 +848,10 @@ MarkerBrowserDialog::update_content ()
mp_ui->browser_frame->set_view (view (), m_cv_index);
mp_ui->browser_frame->enable_updates (true);
std::string tree_state;
view ()->config_get (cfg_rdb_tree_state, tree_state);
mp_ui->browser_frame->set_tree_state (tree_state);
if (rdb) {
// Note: it appears to be required to show the browser page after it has been configured.
// Otherwise the header gets messed up and the configuration is reset.
@ -872,7 +877,11 @@ void
MarkerBrowserDialog::deactivated ()
{
if (lay::Dispatcher::instance ()) {
lay::Dispatcher::instance ()->config_set (cfg_rdb_window_state, lay::save_dialog_state (this).c_str ());
lay::Dispatcher::instance ()->config_set (cfg_rdb_window_state, lay::save_dialog_state (this));
std::string tree_state = mp_ui->browser_frame->get_tree_state ();
if (! tree_state.empty ()) {
lay::Dispatcher::instance ()->config_set (cfg_rdb_tree_state, tree_state);
}
}
mp_ui->browser_frame->set_rdb (0);

View File

@ -1953,7 +1953,83 @@ MarkerBrowserPage::set_rdb (rdb::Database *database)
}
}
void
static std::string top_item_by_index (int i)
{
if (i == 0) {
return std::string ("by-cell");
} else if (i == 1) {
return std::string ("by-category");
} else {
return std::string ();
}
}
static int top_index_from_item (const std::string &s)
{
if (s == "by-cell") {
return 0;
} else if (s == "by-category") {
return 1;
} else {
return -1;
}
}
std::string
MarkerBrowserPage::get_tree_state ()
{
std::string res;
QAbstractItemModel *tree_model = directory_tree->model ();
if (! tree_model) {
return res;
}
int rows = tree_model->rowCount (QModelIndex ());
for (int i = 0; i < rows; ++i) {
bool expanded = directory_tree->isExpanded (tree_model->index (i, 0, QModelIndex ()));
std::string item = top_item_by_index (i);
if (! item.empty ()) {
if (! res.empty ()) {
res += ",";
}
res += expanded ? "+" : "-";
res += item;
}
}
return res;
}
void
MarkerBrowserPage::set_tree_state (const std::string &state)
{
QAbstractItemModel *tree_model = directory_tree->model ();
if (! tree_model) {
return;
}
tl::Extractor ex (state.c_str ());
while (! ex.at_end ()) {
bool expanded = false;
if (ex.test ("+")) {
expanded = true;
} else {
ex.test ("-");
}
std::string item;
if (! ex.try_read_word (item, "-_")) {
break;
}
int index = top_index_from_item (item);
if (index >= 0) {
directory_tree->setExpanded (tree_model->index (index, 0, QModelIndex ()), expanded);
}
ex.test (",");
}
}
void
MarkerBrowserPage::update_content ()
{

View File

@ -161,6 +161,16 @@ public:
*/
void enable_updates (bool f);
/**
* @brief Gets a string with the serialized tree state
*/
std::string get_tree_state ();
/**
* @brief Restores the tree state from the serialized string (see get_tree_state)
*/
void set_tree_state (const std::string &state);
public slots:
void directory_header_clicked (int section);
void directory_sorting_changed (int section, Qt::SortOrder order);

View File

@ -938,6 +938,15 @@ LayoutView::configure (const std::string &name, const std::string &value)
}
return true;
} else if (name == cfg_copy_cell_mode) {
if (mp_hierarchy_panel) {
int m = 0;
tl::from_string (value, m);
mp_hierarchy_panel->set_cell_copy_mode (m);
}
return true;
} else if (name == cfg_cell_list_sorting) {
if (mp_hierarchy_panel) {

View File

@ -44,40 +44,128 @@ namespace db
{
// ------------------------------------------------------------------
// GDS2WriterBase implementation
// Limit checking conversion functions
GDS2WriterBase::GDS2WriterBase ()
static int32_t safe_convert_to_int32 (int32_t value)
{
// .. nothing yet ..
return value;
}
static int safe_scale (double sf, int value)
static int32_t safe_convert_to_int32 (uint32_t value)
{
if (value > std::numeric_limits<int32_t>::max ()) {
throw tl::Exception (tl::to_string (tr ("Coordinate overflow")));
}
return int32_t (value);
}
static int32_t safe_convert_to_int32 (int64_t value)
{
if (value < std::numeric_limits<int32_t>::min ()) {
throw tl::Exception (tl::to_string (tr ("Coordinate underflow")));
}
if (value > std::numeric_limits<int32_t>::max ()) {
throw tl::Exception (tl::to_string (tr ("Coordinate overflow")));
}
return int32_t (value);
}
static int32_t safe_convert_to_int32 (uint64_t value)
{
if (value > std::numeric_limits<int32_t>::max ()) {
throw tl::Exception (tl::to_string (tr ("Coordinate overflow")));
}
return int32_t (value);
}
template <class T>
static int32_t safe_scale (double sf, T value)
{
double i = floor (sf * value + 0.5);
if (i < double (std::numeric_limits<int>::min ())) {
throw tl::Exception ("Scaling failed: coordinate underflow");
if (i < double (std::numeric_limits<int32_t>::min ())) {
throw tl::Exception (tl::to_string (tr ("Scaling failed: coordinate underflow")));
}
if (i > double (std::numeric_limits<int>::max ())) {
throw tl::Exception ("Scaling failed: coordinate overflow");
if (i > double (std::numeric_limits<int32_t>::max ())) {
throw tl::Exception (tl::to_string (tr ("Scaling failed: coordinate overflow")));
}
return int (i);
return int32_t (i);
}
inline int scale (double sf, int value)
template <class T>
inline int32_t scale (double sf, T value)
{
if (sf == 1.0) {
return value;
return safe_convert_to_int32 (value);
} else {
return safe_scale (sf, value);
}
}
static uint16_t safe_convert_to_uint16 (int16_t value)
{
// we accept this as GDS is not well defined here ...
return value;
}
static uint16_t safe_convert_to_uint16 (uint16_t value)
{
return value;
}
static uint16_t safe_convert_to_uint16 (int32_t value)
{
if (value < 0) {
throw tl::Exception (tl::to_string (tr ("Short unsigned integer underflow")));
}
if (value > std::numeric_limits<uint16_t>::max ()) {
throw tl::Exception (tl::to_string (tr ("Short unsigned integer overflow")));
}
return uint16_t (value);
}
static uint16_t safe_convert_to_uint16 (uint32_t value)
{
if (value > std::numeric_limits<uint16_t>::max ()) {
throw tl::Exception (tl::to_string (tr ("Short unsigned integer overflow")));
}
return uint16_t (value);
}
static uint16_t safe_convert_to_uint16 (int64_t value)
{
if (value < 0) {
throw tl::Exception (tl::to_string (tr ("Short unsigned integer underflow")));
}
if (value > std::numeric_limits<uint16_t>::max ()) {
throw tl::Exception (tl::to_string (tr ("Short unsigned integer overflow")));
}
return uint16_t (value);
}
static uint16_t safe_convert_to_uint16 (uint64_t value)
{
if (value > std::numeric_limits<uint16_t>::max ()) {
throw tl::Exception (tl::to_string (tr ("Short unsigned integer overflow")));
}
return uint16_t (value);
}
// ------------------------------------------------------------------
// GDS2WriterBase implementation
GDS2WriterBase::GDS2WriterBase ()
: m_dbu (0.0), m_resolve_skew_arrays (false), m_multi_xy (false), m_no_zero_length_paths (false),
m_max_vertex_count (0), m_write_cell_properties (false), m_keep_instances (false)
{
// .. nothing yet ..
}
void
GDS2WriterBase::write_context_string (size_t n, const std::string &s)
{
// max. size for GDS strings used as payload carrier
size_t chunk_size = 32000;
short max_short = std::numeric_limits<short>::max ();
short max_short = std::numeric_limits<int16_t>::max ();
if (n > size_t (max_short) || s.size () > chunk_size) {
@ -113,7 +201,7 @@ GDS2WriterBase::write_context_string (size_t n, const std::string &s)
write_record_size (6);
write_record (sPROPATTR);
write_short (short (n));
write_short (int16_t (n));
write_string_record (sPROPVALUE, s);
@ -211,13 +299,135 @@ GDS2WriterBase::write_context_cell (db::Layout &layout, const short *time_data,
write_record (sENDSTR);
}
void
GDS2WriterBase::write_shape (const db::Layout &layout, int layer, int datatype, const db::Shape &shape, double sf)
{
if (shape.is_text ()) {
write_text (layer, datatype, sf, m_dbu, shape, layout, shape.prop_id ());
} else if (shape.is_polygon ()) {
write_polygon (layer, datatype, sf, shape, m_multi_xy, m_max_vertex_count, layout, shape.prop_id ());
} else if (shape.is_edge ()) {
write_edge (layer, datatype, sf, shape, layout, shape.prop_id ());
} else if (shape.is_edge_pair ()) {
write_edge (layer, datatype, sf, shape.edge_pair ().first (), layout, shape.prop_id ());
write_edge (layer, datatype, sf, shape.edge_pair ().second (), layout, shape.prop_id ());
} else if (shape.is_path ()) {
if (m_no_zero_length_paths && (shape.path_length () - shape.path_extensions ().first - shape.path_extensions ().second) == 0) {
// eliminate the zero-width path
db::Polygon poly;
shape.polygon (poly);
write_polygon (layer, datatype, sf, poly, m_multi_xy, m_max_vertex_count, layout, shape.prop_id (), false);
} else {
write_path (layer, datatype, sf, shape, m_multi_xy, layout, shape.prop_id ());
}
} else if (shape.is_box ()) {
write_box (layer, datatype, sf, shape, layout, shape.prop_id ());
}
}
void
GDS2WriterBase::write_cell (db::Layout &layout, const db::Cell &cref, const std::vector <std::pair <unsigned int, db::LayerProperties> > &layers, const std::set<db::cell_index_type> &cell_set, double sf, short *time_data)
{
// cell header
write_record_size (4 + 12 * 2);
write_record (sBGNSTR);
write_time (time_data);
write_time (time_data);
try {
write_string_record (sSTRNAME, m_cell_name_map.cell_name (cref.cell_index ()));
} catch (tl::Exception &ex) {
throw tl::Exception (ex.msg () + tl::to_string (tr (", writing cell name")));
}
// cell body
if (m_write_cell_properties && cref.prop_id () != 0) {
try {
write_properties (layout, cref.prop_id ());
} catch (tl::Exception &ex) {
throw tl::Exception (ex.msg () + tl::to_string (tr (", writing layout properties")));
}
}
// instances
for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) {
// write only instances to selected cells
if (m_keep_instances || cell_set.find (inst->cell_index ()) != cell_set.end ()) {
progress_checkpoint ();
try {
write_inst (sf, *inst, true /*normalize*/, m_resolve_skew_arrays, layout, inst->prop_id ());
} catch (tl::Exception &ex) {
throw tl::Exception (ex.msg () + tl::to_string (tr (", writing instances")));
}
}
}
// shapes
for (std::vector <std::pair <unsigned int, db::LayerProperties> >::const_iterator l = layers.begin (); l != layers.end (); ++l) {
if (layout.is_valid_layer (l->first) && l->second.layer >= 0 && l->second.datatype >= 0) {
int layer = l->second.layer;
if (layer > std::numeric_limits<uint16_t>::max ()) {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Cannot write layer numbers larger than %d to GDS2 streams")), int (std::numeric_limits<uint16_t>::max ())));
}
int datatype = l->second.datatype;
if (datatype > std::numeric_limits<uint16_t>::max ()) {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Cannot write datatype numbers larger than %d to GDS2 streams")), int (std::numeric_limits<uint16_t>::max ())));
}
db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::EdgePairs | db::ShapeIterator::Paths | db::ShapeIterator::Texts));
while (! shape.at_end ()) {
progress_checkpoint ();
try {
write_shape (layout, layer, datatype, *shape, sf);
} catch (tl::Exception &ex) {
throw tl::Exception (ex.msg () + tl::sprintf (tl::to_string (tr (", writing layer %d/%d")), layer, datatype));
}
++shape;
}
}
}
// end of cell
write_record_size (4);
write_record (sENDSTR);
}
void
GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options)
{
set_stream (stream);
double dbu = (options.dbu () == 0.0) ? layout.dbu () : options.dbu ();
double sf = options.scale_factor () * (layout.dbu () / dbu);
m_dbu = (options.dbu () == 0.0) ? layout.dbu () : options.dbu ();
double sf = options.scale_factor () * (layout.dbu () / m_dbu);
if (fabs (sf - 1.0) < 1e-9) {
// to avoid rounding problems, set to 1.0 exactly if possible.
sf = 1.0;
@ -225,8 +435,8 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S
db::GDS2WriterOptions gds2_options = options.get_options<db::GDS2WriterOptions> ();
layout.add_meta_info ("dbuu", MetaInfo (tl::to_string (tr ("Database unit in user units")), tl::to_string (dbu / std::max (1e-9, gds2_options.user_units))));
layout.add_meta_info ("dbum", MetaInfo (tl::to_string (tr ("Database unit in meter")), tl::to_string (dbu * 1e-6)));
layout.add_meta_info ("dbuu", MetaInfo (tl::to_string (tr ("Database unit in user units")), tl::to_string (m_dbu / std::max (1e-9, gds2_options.user_units))));
layout.add_meta_info ("dbum", MetaInfo (tl::to_string (tr ("Database unit in meter")), tl::to_string (m_dbu * 1e-6)));
layout.add_meta_info ("libname", MetaInfo (tl::to_string (tr ("Library name")), gds2_options.libname));
std::vector <std::pair <unsigned int, db::LayerProperties> > layers;
@ -265,11 +475,14 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S
layout.add_meta_info ("mod_time", MetaInfo (tl::to_string (tr ("Modification Time")), str_time));
layout.add_meta_info ("access_time", MetaInfo (tl::to_string (tr ("Access Time")), str_time));
bool multi_xy = gds2_options.multi_xy_records;
m_keep_instances = options.keep_instances ();
m_multi_xy = gds2_options.multi_xy_records;
m_max_vertex_count = std::max (gds2_options.max_vertex_count, (unsigned int)4);
m_no_zero_length_paths = gds2_options.no_zero_length_paths;
m_resolve_skew_arrays = gds2_options.resolve_skew_arrays;
m_write_cell_properties = gds2_options.write_cell_properties;
size_t max_cellname_length = std::max (gds2_options.max_cellname_length, (unsigned int)8);
size_t max_vertex_count = std::max (gds2_options.max_vertex_count, (unsigned int)4);
bool no_zero_length_paths = gds2_options.no_zero_length_paths;
bool resolve_skew_arrays = gds2_options.resolve_skew_arrays;
m_cell_name_map = db::WriterCellNameMap (max_cellname_length);
m_cell_name_map.replacement ('$');
@ -302,17 +515,25 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S
write_time (time_data);
write_time (time_data);
write_string_record (sLIBNAME, gds2_options.libname);
try {
write_string_record (sLIBNAME, gds2_options.libname);
} catch (tl::Exception &ex) {
throw tl::Exception (ex.msg () + tl::to_string (tr (", writing LIBNAME")));
}
write_record_size (4 + 8 * 2);
write_record (sUNITS);
write_double (dbu / std::max (1e-9, gds2_options.user_units));
write_double (dbu * 1e-6);
write_double (m_dbu / std::max (1e-9, gds2_options.user_units));
write_double (m_dbu * 1e-6);
// layout properties
if (gds2_options.write_file_properties && layout.prop_id () != 0) {
write_properties (layout, layout.prop_id ());
try {
write_properties (layout, layout.prop_id ());
} catch (tl::Exception &ex) {
throw tl::Exception (ex.msg () + tl::to_string (tr (", writing layout properties")));
}
}
// write context info
@ -327,7 +548,11 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S
}
if (has_context) {
write_context_cell (layout, time_data, cells);
try {
write_context_cell (layout, time_data, cells);
} catch (tl::Exception &ex) {
throw tl::Exception (ex.msg () + tl::to_string (tr (", writing context cell")));
}
}
// body
@ -342,84 +567,12 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S
// also don't write proxy cells which are not employed
if ((! cref.is_ghost_cell () || ! cref.empty ()) && (! cref.is_proxy () || ! cref.is_top ())) {
// cell header
write_record_size (4 + 12 * 2);
write_record (sBGNSTR);
write_time (time_data);
write_time (time_data);
write_string_record (sSTRNAME, m_cell_name_map.cell_name (*cell));
// cell body
if (gds2_options.write_cell_properties && cref.prop_id () != 0) {
write_properties (layout, cref.prop_id ());
try {
write_cell (layout, cref, layers, cell_set, sf, time_data);
} catch (tl::Exception &ex) {
throw tl::Exception (ex.msg () + tl::sprintf (tl::to_string (tr (", writing cell '%s'")), layout.cell_name (*cell)));
}
// instances
for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) {
// write only instances to selected cells
if (options.keep_instances () || cell_set.find (inst->cell_index ()) != cell_set.end ()) {
progress_checkpoint ();
write_inst (sf, *inst, true /*normalize*/, resolve_skew_arrays, layout, inst->prop_id ());
}
}
// shapes
for (std::vector <std::pair <unsigned int, db::LayerProperties> >::const_iterator l = layers.begin (); l != layers.end (); ++l) {
if (layout.is_valid_layer (l->first)) {
int layer = l->second.layer;
int datatype = l->second.datatype;
db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::EdgePairs | db::ShapeIterator::Paths | db::ShapeIterator::Texts));
while (! shape.at_end ()) {
progress_checkpoint ();
if (shape->is_text ()) {
write_text (layer, datatype, sf, dbu, *shape, layout, shape->prop_id ());
} else if (shape->is_polygon ()) {
write_polygon (layer, datatype, sf, *shape, multi_xy, max_vertex_count, layout, shape->prop_id ());
} else if (shape->is_edge ()) {
write_edge (layer, datatype, sf, *shape, layout, shape->prop_id ());
} else if (shape->is_edge_pair ()) {
write_edge (layer, datatype, sf, shape->edge_pair ().first (), layout, shape->prop_id ());
write_edge (layer, datatype, sf, shape->edge_pair ().second (), layout, shape->prop_id ());
} else if (shape->is_path ()) {
if (no_zero_length_paths && (shape->path_length () - shape->path_extensions ().first - shape->path_extensions ().second) == 0) {
// eliminate the zero-width path
db::Polygon poly;
shape->polygon (poly);
write_polygon (layer, datatype, sf, poly, multi_xy, max_vertex_count, layout, shape->prop_id (), false);
} else {
write_path (layer, datatype, sf, *shape, multi_xy, layout, shape->prop_id ());
}
} else if (shape->is_box ()) {
write_box (layer, datatype, sf, *shape, layout, shape->prop_id ());
}
++shape;
}
}
}
// end of cell
write_record_size (4);
write_record (sENDSTR);
}
}
@ -545,8 +698,8 @@ GDS2WriterBase::write_inst (double sf, const db::Instance &instance, bool normal
if (is_reg) {
write_record_size (4 + 2 * 2);
write_record (sCOLROW);
if (amax > 32767 || bmax > 32767) {
throw tl::Exception (tl::to_string (tr ("Cannot write array references with more than 32767 columns or rows to GDS2 streams")));
if (amax > std::numeric_limits<int16_t>::max () || bmax > std::numeric_limits<int16_t>::max ()) {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Cannot write array references with more than %d columns or rows to GDS2 streams")), int (std::numeric_limits<int16_t>::max ())));
}
write_short (std::max ((unsigned long) 1, bmax));
write_short (std::max ((unsigned long) 1, amax));
@ -558,10 +711,10 @@ GDS2WriterBase::write_inst (double sf, const db::Instance &instance, bool normal
write_int (scale (sf, t.disp ().y ()));
if (is_reg) {
write_int (scale (sf, t.disp ().x () + b.x () * bmax));
write_int (scale (sf, t.disp ().y () + b.y () * bmax));
write_int (scale (sf, t.disp ().x () + a.x () * amax));
write_int (scale (sf, t.disp ().y () + a.y () * amax));
write_int (scale (sf, t.disp ().x () + b.x () * (int64_t) bmax));
write_int (scale (sf, t.disp ().y () + b.y () * (int64_t) bmax));
write_int (scale (sf, t.disp ().x () + a.x () * (int64_t) amax));
write_int (scale (sf, t.disp ().y () + a.y () * (int64_t) amax));
}
finish (layout, prop_id);
@ -584,11 +737,11 @@ GDS2WriterBase::write_box (int layer, int datatype, double sf, const db::Shape &
write_record_size (4 + 2);
write_record (sLAYER);
write_short (layer);
write_short (safe_convert_to_uint16 (layer));
write_record_size (4 + 2);
write_record (sDATATYPE);
write_short (datatype);
write_short (safe_convert_to_uint16 (datatype));
write_record_size (4 + 5 * 2 * 4);
write_record (sXY);
@ -618,11 +771,11 @@ GDS2WriterBase::write_path (int layer, int datatype, double sf, const db::Shape
write_record_size (4 + 2);
write_record (sLAYER);
write_short (layer);
write_short (safe_convert_to_uint16 (layer));
write_record_size (4 + 2);
write_record (sDATATYPE);
write_short (datatype);
write_short (safe_convert_to_uint16 (datatype));
short type = 0;
db::Coord w = path.width ();
@ -698,11 +851,11 @@ GDS2WriterBase::write_edge (int layer, int datatype, double sf, const db::Edge &
write_record_size (4 + 2);
write_record (sLAYER);
write_short (layer);
write_short (safe_convert_to_uint16 (layer));
write_record_size (4 + 2);
write_record (sDATATYPE);
write_short (datatype);
write_short (safe_convert_to_uint16 (datatype));
write_record_size (4 + 2);
write_record (sPATHTYPE);
@ -732,11 +885,11 @@ GDS2WriterBase::write_text (int layer, int datatype, double sf, double dbu, cons
write_record_size (4 + 2);
write_record (sLAYER);
write_short (layer);
write_short (safe_convert_to_uint16 (layer));
write_record_size (4 + 2);
write_record (sTEXTTYPE);
write_short (datatype);
write_short (safe_convert_to_uint16 (datatype));
if (shape.text_halign () != db::NoHAlign || shape.text_valign () != db::NoVAlign || shape.text_font () != db::NoFont) {
short ha = short (shape.text_halign () == db::NoHAlign ? db::HAlignLeft : shape.text_halign ());
@ -817,11 +970,11 @@ GDS2WriterBase::write_polygon (int layer, int datatype, double sf, const db::Pol
write_record_size (4 + 2);
write_record (sLAYER);
write_short (layer);
write_short (safe_convert_to_uint16 (layer));
write_record_size (4 + 2);
write_record (sDATATYPE);
write_short (datatype);
write_short (safe_convert_to_uint16 (datatype));
size_t n = polygon.vertices ();
@ -892,11 +1045,11 @@ GDS2WriterBase::write_polygon (int layer, int datatype, double sf, const db::Sha
write_record_size (4 + 2);
write_record (sLAYER);
write_short (layer);
write_short (safe_convert_to_uint16 (layer));
write_record_size (4 + 2);
write_record (sDATATYPE);
write_short (datatype);
write_short (safe_convert_to_uint16 (datatype));
db::Shape::point_iterator e (shape.begin_hull ());
while (n > 0) {
@ -947,11 +1100,11 @@ GDS2WriterBase::write_properties (const db::Layout &layout, db::properties_id_ty
attr = name.to_long ();
}
if (attr >= 0 && attr < 65535) {
if (attr >= 0 && attr <= std::numeric_limits<uint16_t>::max ()) {
write_record_size (6);
write_record (sPROPATTR);
write_short (attr);
write_short ((int16_t) attr);
write_string_record (sPROPVALUE, p->second.to_string ());
@ -974,7 +1127,11 @@ GDS2WriterBase::finish (const db::Layout &layout, db::properties_id_type prop_id
void
GDS2WriterBase::write_string_record (short record, const std::string &t)
{
write_record_size (4 + (int16_t (t.size () + 1) / 2) * 2);
size_t rs = 4 + ((t.size () + 1) / 2) * 2;
if (rs > std::numeric_limits<uint16_t>::max ()) {
throw tl::Exception (tl::to_string (tr ("String max. length overflow")));
}
write_record_size (uint16_t (rs));
write_record (record);
write_string (t);
}

View File

@ -39,6 +39,7 @@ namespace db
class Layout;
class SaveLayoutOptions;
class GDS2WriterOptions;
/**
* @brief A GDS2 writer abstraction
@ -166,10 +167,20 @@ protected:
private:
db::WriterCellNameMap m_cell_name_map;
double m_dbu;
bool m_resolve_skew_arrays;
bool m_multi_xy;
bool m_no_zero_length_paths;
size_t m_max_vertex_count;
bool m_write_cell_properties;
bool m_keep_instances;
void write_properties (const db::Layout &layout, db::properties_id_type prop_id);
void write_context_cell (db::Layout &layout, const short *time_data, const std::vector<cell_index_type> &cells);
void write_context_string (size_t n, const std::string &s);
void write_cell (db::Layout &layout, const db::Cell &cref, const std::vector <std::pair <unsigned int, db::LayerProperties> > &layers,
const std::set <db::cell_index_type> &cell_set, double sf, short *time_data);
void write_shape (const db::Layout &layout, int layer, int datatype, const db::Shape &shape, double sf);
};
} // namespace db

View File

@ -1419,6 +1419,96 @@ TEST(132)
EXPECT_EQ (layout_read.meta_info (ci2, "c").value.to_string (), "-1");
}
// Limits
static std::string run_test_with_error (double sf, db::Layout &layout)
{
try {
tl::OutputMemoryStream buffer;
tl::OutputStream stream (buffer);
db::SaveLayoutOptions options;
options.set_format ("GDS2");
options.set_scale_factor (sf);
db::Writer writer (options);
writer.write (layout, stream);
return std::string ();
} catch (tl::Exception &ex) {
return ex.msg ();
}
}
static std::string huge_string ()
{
std::string n;
for (unsigned int i = 0; i < 100000; ++i) {
n += "A";
}
return n;
}
// Exceeding limits
TEST(140)
{
db::Layout layout;
db::cell_index_type top_index = layout.add_cell ("TOP");
db::Cell &top = layout.cell (top_index);
unsigned int l1 = layout.insert_layer (db::LayerProperties (1, 0));
top.shapes (l1).insert (db::Text (huge_string (), db::Trans ()));
EXPECT_EQ (run_test_with_error (1.0, layout), "String max. length overflow, writing layer 1/0, writing cell 'TOP'");
}
TEST(141)
{
db::Layout layout;
db::cell_index_type top_index = layout.add_cell ("TOP");
db::Cell &top = layout.cell (top_index);
unsigned int l1 = layout.insert_layer (db::LayerProperties (100000, 0));
top.shapes (l1).insert (db::Box (0, 0, 100, 200));
EXPECT_EQ (run_test_with_error (1.0, layout), "Cannot write layer numbers larger than 65535 to GDS2 streams, writing cell 'TOP'");
}
TEST(142)
{
db::Layout layout;
db::cell_index_type top_index = layout.add_cell ("TOP");
db::Cell &top = layout.cell (top_index);
db::cell_index_type child_index = layout.add_cell ("CHILD");
db::Cell &child = layout.cell (child_index);
unsigned int l1 = layout.insert_layer (db::LayerProperties (1, 0));
child.shapes (l1).insert (db::Box (0, 0, 100, 200));
top.insert (db::CellInstArray (child_index, db::Trans (), db::Vector (100000000, 0), db::Vector (0, 100000000), 10, 10));
EXPECT_EQ (run_test_with_error (1.0, layout), ""); // no error
top.clear_insts ();
top.insert (db::CellInstArray (child_index, db::Trans (), db::Vector (100000000, 0), db::Vector (0, 100000000), 100, 100));
EXPECT_EQ (run_test_with_error (1.0, layout), "Coordinate overflow, writing instances, writing cell 'TOP'");
top.clear_insts ();
top.insert (db::CellInstArray (child_index, db::Trans (), db::Vector (100, 0), db::Vector (0, 100), 100000, 100));
EXPECT_EQ (run_test_with_error (1.0, layout), "Cannot write array references with more than 32767 columns or rows to GDS2 streams, writing instances, writing cell 'TOP'");
}
TEST(143)
{
db::Layout layout;
db::cell_index_type top_index = layout.add_cell ("TOP");
db::Cell &top = layout.cell (top_index);
unsigned int l1 = layout.insert_layer (db::LayerProperties (1, 0));
top.shapes (l1).insert (db::Box (-2000000000, 0, 0, 200000000));
EXPECT_EQ (run_test_with_error (1.0, layout), "");
EXPECT_EQ (run_test_with_error (23.0, layout), "Scaling failed: coordinate underflow, writing layer 1/0, writing cell 'TOP'");
}
// Extreme fracturing by max. points
TEST(166)
{
@ -1427,3 +1517,5 @@ TEST(166)
run_test (_this, "t166.oas.gz", "t166_au.gds.gz", false, opt);
}

View File

@ -140,7 +140,7 @@ NetTracerTechComponentEditor::NetTracerTechComponentEditor (QWidget *parent)
connect (action, SIGNAL (triggered ()), this, SLOT (add_clicked ()));
stack_tree->addAction (action);
action = new QAction (QObject::tr ("Delete Selected Stacks"), this);
connect (action, SIGNAL (triggered ()), this, SLOT (delete_clicked ()));
connect (action, SIGNAL (triggered ()), this, SLOT (del_clicked ()));
stack_tree->addAction (action);
action = new QAction (QObject::tr ("Duplicate Stack"), this);
connect (action, SIGNAL (triggered ()), this, SLOT (clone_clicked ()));

View File

@ -684,7 +684,11 @@ PythonInterpreter::trace_func (PyFrameObject *frame, int event, PyObject *arg)
exc_value = PythonPtr (PyTuple_GetItem (arg, 1));
}
if (exc_type && exc_type.get () != PyExc_StopIteration) {
#if PY_VERSION_HEX >= 0x03050000
if (exc_type && exc_type.get () != PyExc_StopIteration && exc_type.get () != PyExc_GeneratorExit && exc_type.get () != PyExc_StopAsyncIteration) {
#else
if (exc_type && exc_type.get () != PyExc_StopIteration && exc_type.get () != PyExc_GeneratorExit) {
#endif
// If the next exception shall be ignored, do so
if (m_ignore_next_exception) {

View File

@ -533,8 +533,13 @@ PyObject *c2python_func<const tl::Variant &>::operator() (const tl::Variant &c)
const gsi::ClassBase *cls = c.gsi_cls ();
if (cls) {
void *obj = const_cast<void *> (c.to_user ());
return object_to_python (obj, 0, c.user_cls ()->gsi_cls (), false, false, true, false);
if (! c.user_is_ref () && cls->is_managed ()) {
void *obj = c.user_unshare ();
return object_to_python (obj, 0, c.user_cls ()->gsi_cls (), true, c.user_is_const (), false, false);
} else {
void *obj = const_cast<void *> (c.to_user ());
return object_to_python (obj, 0, c.user_cls ()->gsi_cls (), false, false, true, false);
}
} else {
// not a known type -> return nil
Py_RETURN_NONE;

View File

@ -60,6 +60,7 @@ class _PCellDeclarationHelperMixin:
self._param_states = None
self._layer_param_index = []
self._layers = []
self._state_stack = []
# public attributes
self.layout = None
self.shape = None
@ -129,6 +130,7 @@ class _PCellDeclarationHelperMixin:
This function delegates the implementation to self.display_text_impl
after configuring the PCellDeclaration object.
"""
self.start()
self._param_values = parameters
try:
text = self.display_text_impl()
@ -165,6 +167,7 @@ class _PCellDeclarationHelperMixin:
"layers" are the layer indexes corresponding to the layer
parameters.
"""
self.start()
self._param_values = None
self._param_states = None
if states:
@ -177,17 +180,39 @@ class _PCellDeclarationHelperMixin:
self._param_values = values
self._layers = layers
def start(self):
"""
Is called to prepare the environment for an operation
After the operation, "finish" must be called.
This method will push the state onto a stack, hence implementing
reentrant implementation methods.
"""
self._state_stack.append( (self._param_values, self._param_states, self._layers, self.cell, self.layout, self.layer, self.shape) )
self._reset_state()
def finish(self):
"""
Is called at the end of an implementation of a PCellDeclaration method
"""
if len(self._state_stack) > 0:
self._param_values, self._param_states, self._layers, self.cell, self.layout, self.layer, self.shape = self._state_stack.pop()
else:
self._reset_state()
def _reset_state(self):
"""
Resets the internal state
"""
self._param_values = None
self._param_states = None
self._layers = None
self._cell = None
self._layout = None
self._layer = None
self._shape = None
self.layout = super(_PCellDeclarationHelperMixin, self).layout()
# This should be here:
# self.cell = None
# self.layer = None
# self.shape = None
# but this would break backward compatibility of "display_text" (actually
# exploiting this bug) - fix this in the next major release.
def get_layers(self, parameters):
"""
@ -255,6 +280,7 @@ class _PCellDeclarationHelperMixin:
The function delegates the implementation to can_create_from_shape_impl
after updating the state of this object with the current parameters.
"""
self.start()
self.layout = layout
self.shape = shape
self.layer = layer
@ -271,6 +297,7 @@ class _PCellDeclarationHelperMixin:
The function delegates the implementation to transformation_from_shape_impl
after updating the state of this object with the current parameters.
"""
self.start()
self.layout = layout
self.shape = shape
self.layer = layer

View File

@ -287,8 +287,13 @@ VALUE c2ruby<tl::Variant> (const tl::Variant &c)
} else if (c.is_user ()) {
const gsi::ClassBase *cls = c.gsi_cls ();
if (cls) {
void *obj = const_cast<void *> (c.to_user ());
return object_to_ruby (obj, 0, c.user_cls ()->gsi_cls (), false, false, true, false);
if (! c.user_is_ref () && cls->is_managed ()) {
void *obj = c.user_unshare ();
return object_to_ruby (obj, 0, c.user_cls ()->gsi_cls (), true, c.user_is_const (), false, false);
} else {
void *obj = const_cast<void *> (c.to_user ());
return object_to_ruby (obj, 0, c.user_cls ()->gsi_cls (), false, false, true, false);
}
} else {
// not a known type -> return nil
return Qnil;

View File

@ -227,6 +227,11 @@ const Object *WeakOrSharedPtr::get () const
return mp_t;
}
void WeakOrSharedPtr::unshare ()
{
m_is_shared = false;
}
void WeakOrSharedPtr::reset_object ()
{
tl::MutexLocker locker (&lock ());

View File

@ -197,6 +197,12 @@ public:
*/
void detach_from_all_events ();
/**
* @brief Unshares the object
* This will turn a shared reference into a weak one.
*/
void unshare ();
/**
* @brief Indicates that this object is an event
* This property is intended for internal use only.
@ -401,6 +407,9 @@ public:
{
// .. nothing yet ..
}
private:
using weak_or_shared_ptr<T, false>::unshare;
};
/**
@ -429,6 +438,9 @@ public:
{
// .. nothing yet ..
}
private:
using weak_or_shared_ptr<T, true>::unshare;
};
}

View File

@ -2723,6 +2723,21 @@ void *Variant::user_take ()
return obj;
}
void *Variant::user_unshare () const
{
tl_assert (is_user () && ! user_is_ref ());
if (m_type == t_user) {
Variant *nc_this = const_cast<Variant *> (this);
nc_this->m_var.mp_user.shared = false;
} else if (m_type == t_user_ref) {
tl::WeakOrSharedPtr *wptr = const_cast<tl::WeakOrSharedPtr *> (reinterpret_cast <const tl::WeakOrSharedPtr *> (m_var.mp_user_ref.ptr));
wptr->unshare ();
}
return const_cast<void *> (to_user ());
}
void Variant::user_assign (const tl::Variant &other)
{
tl_assert (is_user ());

View File

@ -994,6 +994,13 @@ public:
*/
void *user_take ();
/**
* @brief Takes the user object and releases ownership by the variant
* This method is const as it does not change the value, but the ownership of
* the contained object. The object must not be "user_is_ref".
*/
void *user_unshare () const;
/**
* @brief Assigns the object stored in other to self
*

BIN
testdata/algo/break_polygons_test.gds vendored Normal file

Binary file not shown.

BIN
testdata/algo/layout_utils_au_bp1.gds vendored Normal file

Binary file not shown.

BIN
testdata/algo/layout_utils_au_bp2.gds vendored Normal file

Binary file not shown.

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

@ -0,0 +1,14 @@
* Test: dismiss empty top level circuit
; a comment
.subckt top a b
m1 a b a b nmos * a comment
m2 a 'net"q"' \\a net[0] nmos * a comment
mfet\[1\] \a\x41a "net\"q\"" "\\a" net\[0\] nmos w=1.7u ; a comment
.ends
* this triggered generation of a top level circuit
.param p1 17 ; a comment
.end ; a comment

View File

@ -18,6 +18,7 @@
import pya
import unittest
import math
import sys
class BoxPCell(pya.PCellDeclaration):
@ -77,7 +78,7 @@ class PCellTestLib(pya.Library):
sb_cell = self.layout().cell(sb_index)
sb_cell.shapes(l10).insert(pya.Box(0, 0, 100, 200))
# register us with the name "MyLib"
# register us with the name "PCellTestLib"
self.register("PCellTestLib")
@ -135,9 +136,62 @@ if "PCellDeclarationHelper" in pya.__dict__:
# create the PCell declarations
self.layout().register_pcell("Box2", BoxPCell2())
# register us with the name "MyLib"
# register us with the name "PCellTestLib2"
self.register("PCellTestLib2")
# A recursive PCell
class RecursivePCell(pya.PCellDeclarationHelper):
def __init__(self):
super(RecursivePCell, self).__init__()
self.param("layer", self.TypeLayer, "Layer", default = pya.LayerInfo(0, 0))
self.param("line", self.TypeShape, "Line", default = pya.Edge(0, 0, 10000, 0))
self.param("level", self.TypeInt, "Level", default = 1)
def display_text_impl(self):
# provide a descriptive text for the cell
return "RecursivePCell(L=" + str(self.layer) + ",E=" + str(pya.CplxTrans(self.layout.dbu) * self.line) + ",LVL=" + str(self.level)
def produce_impl(self):
# fetch the parameters
l = self.layer_layer
e = self.line
if self.level <= 0:
self.cell.shapes(l).insert(e)
return
d3 = e.d() * (1.0 / 3.0)
d3n = pya.Vector(-d3.y, d3.x)
e1 = pya.Edge(e.p1, e.p1 + d3)
e2 = pya.Edge(e1.p2, e1.p2 + d3 * 0.5 + d3n * math.cos(math.pi / 6))
e3 = pya.Edge(e2.p2, e.p1 + d3 * 2.0)
e4 = pya.Edge(e3.p2, e.p2)
for e in [ e1, e2, e3, e4 ]:
t = pya.Trans(e.p1 - pya.Point())
cc = self.layout.create_cell("RecursivePCell", { "layer": self.layer, "line": t.inverted() * e, "level": self.level - 1 })
self.cell.insert(pya.CellInstArray(cc, t))
class PCellTestLib3(pya.Library):
def __init__(self):
# set the description
self.description = "PCell test lib3"
# create the PCell declarations
self.layout().register_pcell("RecursivePCell", RecursivePCell())
# register us with the name "PCellTestLib3"
self.register("PCellTestLib3")
def inspect_LayerInfo(self):
return "<" + str(self) + ">"
@ -501,6 +555,27 @@ class DBPCellTests(unittest.TestCase):
self.assertEqual(cell.begin_shapes_rec(ly.layer(5, 0)).shape().__str__(), "box (-100,-300;100,300)")
def test_9(self):
if not "PCellDeclarationHelper" in pya.__dict__:
return
# instantiate and register the library
tl = PCellTestLib3()
ly = pya.Layout(True)
li1 = find_layer(ly, "1/0")
self.assertEqual(li1 == None, True)
c1 = ly.create_cell("c1")
c2 = ly.create_cell("RecursivePCell", "PCellTestLib3", { "layer": pya.LayerInfo(1, 0), "level": 4, "line": pya.Edge(0, 0, 20000, 0) })
c1.insert(pya.CellInstArray(c2.cell_index(), pya.Trans()))
self.assertEqual(c2.display_title(), "PCellTestLib3.RecursivePCell(L=1/0,E=(0,0;20,0),LVL=4")
self.assertEqual(str(c1.dbbox()), "(0,0;20,5.774)")
# run unit tests
if __name__ == '__main__':
suite = unittest.TestLoader().loadTestsFromTestCase(DBPCellTests)

View File

@ -81,6 +81,31 @@ class DBRegionTest(unittest.TestCase):
dss = None
self.assertEqual(pya.DeepShapeStore.instance_count(), 0)
# begin_shapes_rec and begin_shapes_merged_rec
def test_extended_iter(self):
r = pya.Region()
# NOTE: this also tests the copy semantics of the RecursiveShape to Variant binding in RBA:
it, trans = r.begin_shapes_rec()
s = ",".join([ str(trans*i.trans()*i.shape().polygon) for i in it.each() ])
self.assertEqual(s, "")
it, trans = r.begin_merged_shapes_rec()
s = ",".join([ str(trans*i.trans()*i.shape().polygon) for i in it.each() ])
self.assertEqual(s, "")
r.insert(pya.Box(0, 0, 100, 100))
r.insert(pya.Box(50, 50, 200, 200))
it, trans = r.begin_shapes_rec()
s = ",".join([ str(trans*i.trans()*i.shape().polygon) for i in it.each() ])
self.assertEqual(s, "(0,0;0,100;100,100;100,0),(50,50;50,200;200,200;200,50)")
it, trans = r.begin_merged_shapes_rec()
s = ",".join([ str(trans*i.trans()*i.shape().polygon) for i in it.each() ])
self.assertEqual(s, "(0,0;0,100;50,100;50,200;200,200;200,50;100,50;100,0)")
# run unit tests
if __name__ == '__main__':
suite = unittest.TestLoader().loadTestsFromTestCase(DBRegionTest)

View File

@ -1334,6 +1334,7 @@ class DBLayoutTests1_TestClass < TestBase
i0 = nil
c0c.each_inst { |i| i.cell_index == l.cell("c1$1").cell_index && i0 = i }
assert_equal(i0.property("p"), 18)
assert_equal(i0.properties, {"p" => 18})
assert_equal(l.cell("c1$1").begin_shapes_rec(0).shape.property("p"), 17)
assert_equal(collect(c0c.begin_shapes_rec(0), l), "[c0$1](0,100;1000,1200)/[c2$1](100,0;1100,1100)/[c3$1](1200,0;2200,1100)/[c3$1](-1200,0;-100,1000)/[c1$1](0,100;1000,1200)")
@ -1379,6 +1380,7 @@ class DBLayoutTests1_TestClass < TestBase
tt = RBA::Trans.new
i0 = c0.insert(RBA::CellInstArray.new(c1.cell_index, tt))
assert_equal(i0.properties, {})
i0.set_property("p", 18)
c0.insert(RBA::CellInstArray.new(c2.cell_index, RBA::Trans.new(RBA::Point.new(100, -100))))
c0.insert(RBA::CellInstArray.new(c3.cell_index, RBA::Trans.new(1)))
@ -2100,6 +2102,48 @@ class DBLayoutTests1_TestClass < TestBase
end
# break_polygons
def test_25
def shapes2str(shapes)
str = []
shapes.each do |s|
str << s.to_s
end
str.join(";")
end
ly = RBA::Layout::new
top = ly.create_cell("TOP")
l1 = ly.layer(1, 0)
l2 = ly.layer(2, 0)
top.shapes(l1).insert(RBA::Polygon::new([ [0, 0], [0, 10000], [10000, 10000], [10000, 9000], [1000, 9000], [1000, 0] ]))
top.shapes(l2).insert(RBA::Polygon::new([ [0, 0], [0, 10000], [10000, 10000], [10000, 9000], [1000, 9000], [1000, 0] ]))
assert_equal(shapes2str(top.shapes(l1)), "polygon (0,0;0,10000;10000,10000;10000,9000;1000,9000;1000,0)")
assert_equal(shapes2str(top.shapes(l2)), "polygon (0,0;0,10000;10000,10000;10000,9000;1000,9000;1000,0)")
s1 = top.shapes(l1).dup
assert_equal(shapes2str(s1), "polygon (0,0;0,10000;10000,10000;10000,9000;1000,9000;1000,0)")
s1.break_polygons(10, 3.0)
assert_equal(shapes2str(s1), "polygon (0,0;0,9000;1000,9000;1000,0);polygon (0,9000;0,10000;10000,10000;10000,9000)")
ly2 = ly.dup
top2 = ly2.top_cell
ly.break_polygons(10, 3.0)
assert_equal(shapes2str(top.shapes(l1)), "polygon (0,0;0,9000;1000,9000;1000,0);polygon (0,9000;0,10000;10000,10000;10000,9000)")
assert_equal(shapes2str(top.shapes(l2)), "polygon (0,0;0,9000;1000,9000;1000,0);polygon (0,9000;0,10000;10000,10000;10000,9000)")
ly2.break_polygons(ly2.layer(1, 0), 10, 3.0)
assert_equal(shapes2str(top2.shapes(ly2.layer(1, 0))), "polygon (0,0;0,9000;1000,9000;1000,0);polygon (0,9000;0,10000;10000,10000;10000,9000)")
assert_equal(shapes2str(top2.shapes(ly2.layer(2, 0))), "polygon (0,0;0,10000;10000,10000;10000,9000;1000,9000;1000,0)")
end
# Iterating while flatten
def test_issue200

View File

@ -674,6 +674,7 @@ class DBLayoutTests2_TestClass < TestBase
lindex = ly.insert_layer( linfo )
c1 = ly.cell( ci1 )
assert_equal( c1.properties, {} )
c2 = ly.cell( ci2 )
tr = RBA::Trans::new
inst = c2.insert( RBA::CellInstArray::new( c1.cell_index, tr ) )
@ -701,6 +702,7 @@ class DBLayoutTests2_TestClass < TestBase
c1.prop_id = pid
assert_equal( c1.prop_id, pid )
assert_equal( c1.property( 17 ).inspect, "\"a\"" )
assert_equal( c1.properties, { 17 => "a", "b" => [1, 5, 7] } )
c1.set_property( 5, 23 )
c1.delete_property( 17 )
assert_equal( c1.property( 17 ).inspect, "nil" )
@ -1027,6 +1029,7 @@ class DBLayoutTests2_TestClass < TestBase
def test_11
ly = RBA::Layout::new
assert_equal(ly.properties, {})
assert_equal(ly.prop_id, 0)
ly.prop_id = 1
@ -1037,6 +1040,7 @@ class DBLayoutTests2_TestClass < TestBase
ly.set_property("x", 1)
assert_equal(ly.prop_id, 1)
assert_equal(ly.property("x"), 1)
assert_equal(ly.properties, {"x" => 1})
ly.set_property("x", 17)
assert_equal(ly.prop_id, 2)
assert_equal(ly.property("x"), 17)

View File

@ -832,18 +832,25 @@ END
nl = RBA::Netlist::new
assert_equal(nl.top_circuit_count, 0)
assert_equal(nl.top_circuit == nil, true)
c1 = RBA::Circuit::new
c1.name = "C1"
c1.cell_index = 17
nl.add(c1)
assert_equal(nl.top_circuit_count, 1)
assert_equal(nl.top_circuit.name, "C1")
c2 = RBA::Circuit::new
c2.name = "C2"
c1.cell_index = 42
nl.add(c2)
assert_equal(nl.top_circuit_count, 2)
begin
nl.top_circuit
assert_equal(true, false)
rescue
end
c3 = RBA::Circuit::new
c3.name = "C3"
@ -854,6 +861,10 @@ END
nl.each_circuit_top_down { |c| names << c.name }
assert_equal(names.join(","), "C3,C2,C1")
names = []
nl.top_circuits.each { |c| names << c.name }
assert_equal(names.join(","), "C3,C2,C1")
names = []
nl.each_circuit_bottom_up { |c| names << c.name }
assert_equal(names.join(","), "C1,C2,C3")

View File

@ -170,6 +170,71 @@ if RBA.constants.member?(:PCellDeclarationHelper)
end
# A recursive PCell
class RecursivePCell < RBA::PCellDeclarationHelper
def initialize
super()
param("layer", RecursivePCell::TypeLayer, "Layer", :default => RBA::LayerInfo::new(0, 0))
param("line", RecursivePCell::TypeShape, "Line", :default => RBA::Edge::new(0, 0, 10000, 0))
param("level", RecursivePCell::TypeInt, "Level", :default => 1)
end
def display_text_impl
# provide a descriptive text for the cell
return "RecursivePCell(L=" + self.layer.to_s + ",E=" + (RBA::CplxTrans::new(self.layout.dbu) * self.line).to_s + ",LVL=" + self.level.to_s
end
def produce_impl
# fetch the parameters
l = self.layer_layer
e = self.line
if self.level <= 0
self.cell.shapes(l).insert(e)
return
end
d3 = e.d * (1.0 / 3.0)
d3n = RBA::Vector::new(-d3.y, d3.x)
e1 = RBA::Edge::new(e.p1, e.p1 + d3)
e2 = RBA::Edge::new(e1.p2, e1.p2 + d3 * 0.5 + d3n * Math::cos(Math::PI / 6))
e3 = RBA::Edge::new(e2.p2, e.p1 + d3 * 2.0)
e4 = RBA::Edge::new(e3.p2, e.p2)
[ e1, e2, e3, e4 ].each do |e|
t = RBA::Trans::new(e.p1 - RBA::Point::new)
cc = self.layout.create_cell("RecursivePCell", { "layer" => self.layer, "line" => t.inverted * e, "level" => self.level - 1 })
self.cell.insert(RBA::CellInstArray::new(cc, t))
end
end
end
class PCellTestLib3 < RBA::Library
def initialize
# set the description
self.description = "PCell test lib3"
# create the PCell declarations
self.layout().register_pcell("RecursivePCell", RecursivePCell::new)
# register us with the name "PCellTestLib3"
self.register("PCellTestLib3")
end
end
end
# A helper for testing: provide an inspect method
@ -809,6 +874,30 @@ class DBPCell_TestClass < TestBase
end
def test_12
if !RBA.constants.member?(:PCellDeclarationHelper)
return
end
# instantiate and register the library
tl = PCellTestLib3::new
ly = RBA::Layout::new
li1 = ly.find_layer("1/0")
assert_equal(li1 == nil, true)
c1 = ly.create_cell("c1")
c2 = ly.create_cell("RecursivePCell", "PCellTestLib3", { "layer" => RBA::LayerInfo::new(1, 0), "level" => 4, "line" => RBA::Edge::new(0, 0, 20000, 0) })
c1.insert(RBA::CellInstArray::new(c2.cell_index(), RBA::Trans::new))
assert_equal(c2.display_title, "PCellTestLib3.RecursivePCell(L=1/0,E=(0,0;20,0),LVL=4")
assert_equal(c1.dbbox.to_s, "(0,0;20,5.774)")
end
end
load("test_epilogue.rb")

View File

@ -1471,6 +1471,33 @@ class DBRegion_TestClass < TestBase
end
# begin_shapes_rec and begin_shapes_merged_rec
def test_extended_iter
r = RBA::Region::new()
# NOTE: this also tests the copy semantics of the RecursiveShape to Variant binding in RBA:
iter, trans = r.begin_shapes_rec
str = iter.each.collect { |i| (trans*i.trans*i.shape.polygon).to_s }.join(",")
assert_equal(str, "")
iter, trans = r.begin_merged_shapes_rec
str = iter.each.collect { |i| (trans*i.trans*i.shape.polygon).to_s }.join(",")
assert_equal(str, "")
r.insert(RBA::Box::new(0, 0, 100, 100))
r.insert(RBA::Box::new(50, 50, 200, 200))
iter, trans = r.begin_shapes_rec
str = iter.each.collect { |i| (trans*i.trans*i.shape.polygon).to_s }.join(",")
assert_equal(str, "(0,0;0,100;100,100;100,0),(50,50;50,200;200,200;200,50)")
iter, trans = r.begin_merged_shapes_rec
str = iter.each.collect { |i| (trans*i.trans*i.shape.polygon).to_s }.join(",")
assert_equal(str, "(0,0;0,100;50,100;50,200;200,200;200,50;100,50;100,0)")
end
end
load("test_epilogue.rb")

View File

@ -1656,6 +1656,28 @@ class DBShapes_TestClass < TestBase
end
# Shape objects and properties
def test_13
ly = RBA::Layout::new
l1 = ly.layer(1, 0)
tc = ly.create_cell("TOP")
sh = tc.shapes(l1).insert(RBA::Box::new(0, 0, 100, 200))
assert_equal(sh.property("k").inspect, "nil")
assert_equal(sh.properties.inspect, "{}")
sh.set_property("k", 17)
assert_equal(sh.property("k").inspect, "17")
assert_equal(sh.property("u").inspect, "nil")
assert_equal(sh.properties.inspect, "{\"k\"=>17}")
sh.set_property("u", "42")
assert_equal(sh.properties.inspect, "{\"k\"=>17, \"u\"=>\"42\"}")
end
end
load("test_epilogue.rb")