Merge pull request #1811 from KLayout/wip

Wip
This commit is contained in:
Matthias Köfferlein 2024-07-30 18:59:09 +02:00 committed by GitHub
commit 8ab398dc58
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
55 changed files with 1294 additions and 334 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

BIN
testdata/algo/break_polygons_test.gds vendored Normal file

Binary file not shown.

BIN
testdata/algo/layout_utils_au_bp1.gds vendored Normal file

Binary file not shown.

BIN
testdata/algo/layout_utils_au_bp2.gds vendored Normal file

Binary file not shown.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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