mirror of https://github.com/KLayout/klayout.git
Merge branch 'master' of github.com:KLayout/klayout
This commit is contained in:
commit
1040e81fe6
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -272,6 +272,11 @@ module RBA
|
|||
# of a PCell
|
||||
class PCellDeclarationHelper < 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 << [ @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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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 ());
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ namespace db
|
|||
// Recursive shape iterator implementation
|
||||
|
||||
RecursiveShapeIterator::RecursiveShapeIterator (const RecursiveShapeIterator &d)
|
||||
: gsi::ObjectBase (d)
|
||||
{
|
||||
operator= (d);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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> ()
|
||||
,
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 ()));
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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")));
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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"));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -579,7 +579,7 @@ CopyCellModeDialog::~CopyCellModeDialog ()
|
|||
}
|
||||
|
||||
bool
|
||||
CopyCellModeDialog::exec_dialog (int ©_mode)
|
||||
CopyCellModeDialog::exec_dialog (int ©_mode, bool &dont_ask)
|
||||
{
|
||||
QRadioButton *buttons [] = { mp_ui->shallow_rb, mp_ui->deep_rb };
|
||||
|
||||
|
|
@ -592,6 +592,7 @@ CopyCellModeDialog::exec_dialog (int ©_mode)
|
|||
if (buttons [i]->isChecked ()) {
|
||||
copy_mode = i;
|
||||
}
|
||||
dont_ask = mp_ui->dont_ask_cbx->isChecked ();
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ public:
|
|||
*
|
||||
* The mode is either 0 (for shallow), 1 (for deep)
|
||||
*/
|
||||
bool exec_dialog (int ©_mode);
|
||||
bool exec_dialog (int ©_mode, bool &dont_ask_again);
|
||||
|
||||
private:
|
||||
Ui::CopyCellModeDialog *mp_ui;
|
||||
|
|
|
|||
|
|
@ -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> ();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)));
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ FORMS = \
|
|||
LayoutViewConfigPage6.ui \
|
||||
LayoutViewConfigPage7.ui \
|
||||
LayoutViewConfigPage.ui \
|
||||
LayoutViewConfigPage8.ui \
|
||||
LibraryCellSelectionForm.ui \
|
||||
LoadLayoutOptionsDialog.ui \
|
||||
MarkerBrowserConfigPage2.ui \
|
||||
|
|
|
|||
|
|
@ -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"));
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 ()
|
||||
{
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ()));
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 ());
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ());
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -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
|
||||
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
Loading…
Reference in New Issue