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:
Matthias Koefferlein 2021-12-26 12:20:54 +01:00
parent 812e26aff9
commit 8e02b400ca
5 changed files with 140 additions and 142 deletions

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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 ();

View File

@ -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);
}