mirror of https://github.com/KLayout/klayout.git
Fixed #951
The problem was essentially the "specific path" which got invalid. The solution is to establish a better criterion for "path validity" and use for failsafes against invalid paths. In addition, the path validation has been modified such that a better choice is made about the remaining path after a cell gets deleted.
This commit is contained in:
parent
812e26aff9
commit
8e02b400ca
|
|
@ -2412,127 +2412,131 @@ PartialService::selection_to_view ()
|
|||
|
||||
}
|
||||
|
||||
// build the transformation variants cache
|
||||
TransformationVariants tv (view ());
|
||||
|
||||
size_t n_marker = 0;
|
||||
size_t n_inst_marker = 0;
|
||||
|
||||
for (partial_objects::const_iterator r = m_selection.begin (); r != m_selection.end (); ++r) {
|
||||
if (! m_selection.empty ()) {
|
||||
|
||||
const lay::CellView &cv = view ()->cellview (r->first.cv_index ());
|
||||
// build the transformation variants cache
|
||||
TransformationVariants tv (view ());
|
||||
|
||||
if (! r->first.is_cell_inst ()) {
|
||||
for (partial_objects::const_iterator r = m_selection.begin (); r != m_selection.end (); ++r) {
|
||||
|
||||
const std::vector<db::DCplxTrans> *tv_list = tv.per_cv_and_layer (r->first.cv_index (), r->first.layer ());
|
||||
if (tv_list && !tv_list->empty ()) {
|
||||
const lay::CellView &cv = view ()->cellview (r->first.cv_index ());
|
||||
|
||||
// use only the first one of the explicit transformations
|
||||
// TODO: clarify how this can be implemented in a more generic form or leave it thus.
|
||||
db::ICplxTrans gt (cv.context_trans () * r->first.trans ());
|
||||
db::CplxTrans tt = (*tv_list) [0] * db::CplxTrans (cv->layout ().dbu ()) * gt;
|
||||
db::Vector move_vector (tt.inverted () * (move_trans * (tt * db::Point ())));
|
||||
if (! r->first.is_cell_inst ()) {
|
||||
|
||||
// create the shift sets describing how points and edges are being moved
|
||||
|
||||
std::map <EdgeWithIndex, db::Edge> new_edges;
|
||||
std::map <PointWithIndex, db::Point> new_points;
|
||||
const std::vector<db::DCplxTrans> *tv_list = tv.per_cv_and_layer (r->first.cv_index (), r->first.layer ());
|
||||
if (tv_list && !tv_list->empty ()) {
|
||||
|
||||
if (m_dragging) {
|
||||
create_shift_sets (r->first.shape (), r->second, new_points, new_edges, move_vector);
|
||||
}
|
||||
// use only the first one of the explicit transformations
|
||||
// TODO: clarify how this can be implemented in a more generic form or leave it thus.
|
||||
db::ICplxTrans gt (cv.context_trans () * r->first.trans ());
|
||||
db::CplxTrans tt = (*tv_list) [0] * db::CplxTrans (cv->layout ().dbu ()) * gt;
|
||||
db::Vector move_vector (tt.inverted () * (move_trans * (tt * db::Point ())));
|
||||
|
||||
// create the markers to represent vertices and edges
|
||||
// create the shift sets describing how points and edges are being moved
|
||||
|
||||
enter_vertices (n_marker, r, new_points, new_edges, gt, *tv_list, false);
|
||||
std::map <EdgeWithIndex, db::Edge> new_edges;
|
||||
std::map <PointWithIndex, db::Point> new_points;
|
||||
|
||||
if (r->first.shape ().is_polygon ()) {
|
||||
if (m_dragging) {
|
||||
create_shift_sets (r->first.shape (), r->second, new_points, new_edges, move_vector);
|
||||
}
|
||||
|
||||
for (unsigned int c = 0; c < r->first.shape ().holes () + 1; ++c) {
|
||||
// create the markers to represent vertices and edges
|
||||
|
||||
enter_vertices (n_marker, r, new_points, new_edges, gt, *tv_list, false);
|
||||
|
||||
if (r->first.shape ().is_polygon ()) {
|
||||
|
||||
for (unsigned int c = 0; c < r->first.shape ().holes () + 1; ++c) {
|
||||
|
||||
unsigned int n = 0;
|
||||
db::Shape::polygon_edge_iterator ee;
|
||||
for (db::Shape::polygon_edge_iterator e = r->first.shape ().begin_edge (c); ! e.at_end (); e = ee, ++n) {
|
||||
ee = e;
|
||||
++ee;
|
||||
unsigned int nn = ee.at_end () ? 0 : n + 1;
|
||||
enter_edge (EdgeWithIndex (*e, n, nn, c), n_marker, r, new_points, new_edges, gt, *tv_list, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
db::Polygon poly;
|
||||
r->first.shape ().polygon (poly);
|
||||
|
||||
// warning: poly is modified:
|
||||
enter_polygon (poly, n_marker, r, new_points, new_edges, gt, *tv_list, false);
|
||||
|
||||
} else if (r->first.shape ().is_path ()) {
|
||||
|
||||
if (r->first.shape ().begin_point () != r->first.shape ().end_point ()) {
|
||||
|
||||
db::Shape::point_iterator pt = r->first.shape ().begin_point ();
|
||||
db::Point p1 = *pt;
|
||||
|
||||
unsigned int n = 0;
|
||||
while (true) {
|
||||
|
||||
++pt;
|
||||
if (pt == r->first.shape ().end_point ()) {
|
||||
break;
|
||||
}
|
||||
|
||||
enter_edge (EdgeWithIndex (db::Edge (p1, *pt), n, n + 1, 0), n_marker, r, new_points, new_edges, gt, *tv_list, false);
|
||||
|
||||
p1 = *pt;
|
||||
++n;
|
||||
|
||||
}
|
||||
|
||||
// TODO: ... put this somewhere else:
|
||||
db::Path path;
|
||||
r->first.shape ().path (path);
|
||||
|
||||
// warning: path is modified:
|
||||
enter_path (path, n_marker, r, new_points, new_edges, gt, *tv_list, false);
|
||||
|
||||
}
|
||||
|
||||
} else if (r->first.shape ().is_box ()) {
|
||||
|
||||
// convert to polygon and test those edges
|
||||
db::Polygon poly (r->first.shape ().box ());
|
||||
unsigned int n = 0;
|
||||
db::Shape::polygon_edge_iterator ee;
|
||||
for (db::Shape::polygon_edge_iterator e = r->first.shape ().begin_edge (c); ! e.at_end (); e = ee, ++n) {
|
||||
for (db::Shape::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); e = ee, ++n) {
|
||||
ee = e;
|
||||
++ee;
|
||||
unsigned int nn = ee.at_end () ? 0 : n + 1;
|
||||
enter_edge (EdgeWithIndex (*e, n, nn, c), n_marker, r, new_points, new_edges, gt, *tv_list, false);
|
||||
enter_edge (EdgeWithIndex (*e, n, nn, 0), n_marker, r, new_points, new_edges, gt, *tv_list, false);
|
||||
}
|
||||
|
||||
}
|
||||
// warning: poly is modified:
|
||||
enter_polygon (poly, n_marker, r, new_points, new_edges, gt, *tv_list, false);
|
||||
|
||||
db::Polygon poly;
|
||||
r->first.shape ().polygon (poly);
|
||||
} else if (r->first.shape ().is_text ()) {
|
||||
|
||||
// warning: poly is modified:
|
||||
enter_polygon (poly, n_marker, r, new_points, new_edges, gt, *tv_list, false);
|
||||
|
||||
} else if (r->first.shape ().is_path ()) {
|
||||
|
||||
if (r->first.shape ().begin_point () != r->first.shape ().end_point ()) {
|
||||
|
||||
db::Shape::point_iterator pt = r->first.shape ().begin_point ();
|
||||
db::Point p1 = *pt;
|
||||
|
||||
unsigned int n = 0;
|
||||
while (true) {
|
||||
|
||||
++pt;
|
||||
if (pt == r->first.shape ().end_point ()) {
|
||||
break;
|
||||
}
|
||||
|
||||
enter_edge (EdgeWithIndex (db::Edge (p1, *pt), n, n + 1, 0), n_marker, r, new_points, new_edges, gt, *tv_list, false);
|
||||
|
||||
p1 = *pt;
|
||||
++n;
|
||||
|
||||
}
|
||||
|
||||
// TODO: ... put this somewhere else:
|
||||
db::Path path;
|
||||
r->first.shape ().path (path);
|
||||
|
||||
// warning: path is modified:
|
||||
enter_path (path, n_marker, r, new_points, new_edges, gt, *tv_list, false);
|
||||
db::Point tp (r->first.shape ().text_trans () * db::Point ());
|
||||
enter_edge (EdgeWithIndex (db::Edge (tp, tp), 0, 0, 0), n_marker, r, new_points, new_edges, gt, *tv_list, false);
|
||||
|
||||
}
|
||||
|
||||
} else if (r->first.shape ().is_box ()) {
|
||||
|
||||
// convert to polygon and test those edges
|
||||
db::Polygon poly (r->first.shape ().box ());
|
||||
unsigned int n = 0;
|
||||
db::Shape::polygon_edge_iterator ee;
|
||||
for (db::Shape::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); e = ee, ++n) {
|
||||
ee = e;
|
||||
++ee;
|
||||
unsigned int nn = ee.at_end () ? 0 : n + 1;
|
||||
enter_edge (EdgeWithIndex (*e, n, nn, 0), n_marker, r, new_points, new_edges, gt, *tv_list, false);
|
||||
}
|
||||
|
||||
// warning: poly is modified:
|
||||
enter_polygon (poly, n_marker, r, new_points, new_edges, gt, *tv_list, false);
|
||||
|
||||
} else if (r->first.shape ().is_text ()) {
|
||||
|
||||
db::Point tp (r->first.shape ().text_trans () * db::Point ());
|
||||
enter_edge (EdgeWithIndex (db::Edge (tp, tp), 0, 0, 0), n_marker, r, new_points, new_edges, gt, *tv_list, false);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
|
||||
} else {
|
||||
// compute the global transformation including movement, context and explicit transformation
|
||||
db::ICplxTrans gt = db::VCplxTrans (1.0 / cv->layout ().dbu ()) * db::DCplxTrans (move_trans) * db::CplxTrans (cv->layout ().dbu ());
|
||||
gt *= (cv.context_trans () * r->first.trans ());
|
||||
|
||||
// compute the global transformation including movement, context and explicit transformation
|
||||
db::ICplxTrans gt = db::VCplxTrans (1.0 / cv->layout ().dbu ()) * db::DCplxTrans (move_trans) * db::CplxTrans (cv->layout ().dbu ());
|
||||
gt *= (cv.context_trans () * r->first.trans ());
|
||||
const std::vector<db::DCplxTrans> *tv_list = tv.per_cv (r->first.cv_index ());
|
||||
if (tv_list && ! tv_list->empty ()) {
|
||||
lay::InstanceMarker *marker = new_inst_marker (n_inst_marker, r->first.cv_index (), false);
|
||||
marker->set (r->first.back ().inst_ptr, gt, *tv_list);
|
||||
}
|
||||
|
||||
const std::vector<db::DCplxTrans> *tv_list = tv.per_cv (r->first.cv_index ());
|
||||
if (tv_list && ! tv_list->empty ()) {
|
||||
lay::InstanceMarker *marker = new_inst_marker (n_inst_marker, r->first.cv_index (), false);
|
||||
marker->set (r->first.back ().inst_ptr, gt, *tv_list);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -501,13 +501,13 @@ CellView::is_valid () const
|
|||
}
|
||||
|
||||
// check, if the path references valid cell indices.
|
||||
for (specific_cell_path_type::const_iterator pp = m_specific_path.begin (); pp != m_specific_path.end (); ++pp) {
|
||||
if (! m_layout_href.get ()->layout ().is_valid_cell_index (pp->inst_ptr.cell_index ())) {
|
||||
for (unspecific_cell_path_type::const_iterator pp = m_unspecific_path.begin (); pp != m_unspecific_path.end (); ++pp) {
|
||||
if (! m_layout_href.get ()->layout ().is_valid_cell_index (*pp)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (unspecific_cell_path_type::const_iterator pp = m_unspecific_path.begin (); pp != m_unspecific_path.end (); ++pp) {
|
||||
if (! m_layout_href.get ()->layout ().is_valid_cell_index (*pp)) {
|
||||
for (specific_cell_path_type::const_iterator pp = m_specific_path.begin (); pp != m_specific_path.end (); ++pp) {
|
||||
if (! pp->inst_ptr.instances () || ! pp->inst_ptr.instances ()->is_valid (pp->inst_ptr) || ! m_layout_href.get ()->layout ().is_valid_cell_index (pp->inst_ptr.cell_index ())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -836,6 +836,8 @@ HierarchyControlPanel::do_update_content (int cv_index)
|
|||
if (&m_cellviews [i]->layout () != &mp_view->cellview (i)->layout ()) {
|
||||
m_needs_update [i] = true;
|
||||
m_force_close [i] = true;
|
||||
} else if (! m_cellviews [i].is_valid ()) {
|
||||
m_needs_update [i] = true;
|
||||
} else if (m_cellviews [i].combined_unspecific_path () != mp_view->cellview (i).combined_unspecific_path ()) {
|
||||
m_needs_update [i] = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4896,6 +4896,7 @@ LayoutView::select_cell_fit (const cell_path_type &path, int index)
|
|||
|
||||
set_min_hier_levels (0);
|
||||
cancel ();
|
||||
cellview_iter (index)->set_specific_path (lay::CellView::specific_cell_path_type ());
|
||||
cellview_iter (index)->set_unspecific_path (path);
|
||||
set_active_cellview_index (index);
|
||||
redraw ();
|
||||
|
|
@ -4985,6 +4986,7 @@ LayoutView::select_cell (const cell_path_type &path, int index)
|
|||
|
||||
set_min_hier_levels (0);
|
||||
cancel ();
|
||||
cellview_iter (index)->set_specific_path (lay::CellView::specific_cell_path_type ());
|
||||
cellview_iter (index)->set_unspecific_path (path);
|
||||
set_active_cellview_index (index);
|
||||
redraw ();
|
||||
|
|
|
|||
|
|
@ -42,6 +42,41 @@
|
|||
namespace lay
|
||||
{
|
||||
|
||||
static void
|
||||
collect_cells_to_delete (const db::Layout &layout, const db::Cell &cell, std::set<db::cell_index_type> &called)
|
||||
{
|
||||
// don't delete proxies - they are deleted later when the layout is cleaned
|
||||
for (db::Cell::child_cell_iterator cc = cell.begin_child_cells (); ! cc.at_end (); ++cc) {
|
||||
if (called.find (*cc) == called.end () && !layout.cell (*cc).is_proxy ()) {
|
||||
called.insert (*cc);
|
||||
collect_cells_to_delete (layout, layout.cell (*cc), called);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
validate_cell_path (const db::Layout &layout, lay::LayoutView::cell_path_type &path)
|
||||
{
|
||||
for (size_t i = 0; i < path.size (); ++i) {
|
||||
|
||||
if (! layout.is_valid_cell_index (path [i])) {
|
||||
|
||||
if (layout.is_valid_cell_index (path.back ())) {
|
||||
// use a stub path
|
||||
path.erase (path.begin (), --path.end ());
|
||||
} else {
|
||||
// strip everything that is not valid
|
||||
path.erase (path.begin () + i, path.end ());
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
LayoutViewFunctions::LayoutViewFunctions (db::Manager *manager, LayoutView *view)
|
||||
: lay::Plugin (view), mp_view (view), mp_manager (manager)
|
||||
{
|
||||
|
|
@ -493,18 +528,7 @@ LayoutViewFunctions::cm_cell_replace ()
|
|||
|
||||
view ()->commit ();
|
||||
|
||||
// If one of the cells in the path was deleted, establish a valid path
|
||||
|
||||
bool needs_update = false;
|
||||
for (size_t i = cell_path.size (); i > 0; ) {
|
||||
--i;
|
||||
if (! layout.is_valid_cell_index (cell_path [i])) {
|
||||
cell_path.erase (cell_path.begin () + i, cell_path.end ());
|
||||
needs_update = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (needs_update) {
|
||||
if (validate_cell_path (layout, cell_path)) {
|
||||
view ()->select_cell (cell_path, cv_index);
|
||||
}
|
||||
|
||||
|
|
@ -613,36 +637,13 @@ LayoutViewFunctions::cm_cell_convert_to_static ()
|
|||
|
||||
view ()->commit ();
|
||||
|
||||
// If one of the cells in the path was deleted, establish a valid path
|
||||
|
||||
bool needs_update = false;
|
||||
for (size_t i = cell_path.size (); i > 0; ) {
|
||||
--i;
|
||||
if (! layout.is_valid_cell_index (cell_path [i])) {
|
||||
cell_path.erase (cell_path.begin () + i, cell_path.end ());
|
||||
needs_update = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (needs_update) {
|
||||
if (validate_cell_path (layout, cell_path)) {
|
||||
view ()->select_cell (cell_path, cv_index);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
collect_cells_to_delete (const db::Layout &layout, const db::Cell &cell, std::set<db::cell_index_type> &called)
|
||||
{
|
||||
// don't delete proxies - they are deleted later when the layout is cleaned
|
||||
for (db::Cell::child_cell_iterator cc = cell.begin_child_cells (); ! cc.at_end (); ++cc) {
|
||||
if (called.find (*cc) == called.end () && !layout.cell (*cc).is_proxy ()) {
|
||||
called.insert (*cc);
|
||||
collect_cells_to_delete (layout, layout.cell (*cc), called);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LayoutViewFunctions::cm_cell_delete ()
|
||||
{
|
||||
|
|
@ -704,18 +705,7 @@ LayoutViewFunctions::cm_cell_delete ()
|
|||
|
||||
view ()->commit ();
|
||||
|
||||
// If one of the cells in the path was deleted, establish a valid path
|
||||
|
||||
bool needs_update = false;
|
||||
for (size_t i = cell_path.size (); i > 0; ) {
|
||||
--i;
|
||||
if (! layout.is_valid_cell_index (cell_path [i])) {
|
||||
cell_path.erase (cell_path.begin () + i, cell_path.end ());
|
||||
needs_update = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (needs_update) {
|
||||
if (validate_cell_path (layout, cell_path)) {
|
||||
view ()->select_cell (cell_path, cv_index);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue