mirror of https://github.com/KLayout/klayout.git
Enhance the selection behavior of partial edit mode: allow selection of edge ends if edges overlap, graphical indicator for selected partial
This commit is contained in:
parent
67436d81a5
commit
c831ed15f8
|
|
@ -774,7 +774,6 @@ PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, co
|
|||
|
||||
checkpoint ();
|
||||
|
||||
// in point mode just store that found that has the least "distance"
|
||||
m_founds.push_back (founds_vector_type::value_type ());
|
||||
|
||||
lay::ObjectInstPath &inst_path = m_founds.back ().first;
|
||||
|
|
@ -786,7 +785,8 @@ PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, co
|
|||
inst_path.set_layer (*l);
|
||||
inst_path.set_shape (*shape);
|
||||
|
||||
// in point mode, test the edges and use a "closest" criterion
|
||||
// in box mode, select the edges depending on whether an endpoint is inside the
|
||||
// box or not
|
||||
if (shape->is_polygon ()) {
|
||||
|
||||
for (unsigned int c = 0; c < shape->holes () + 1; ++c) {
|
||||
|
|
@ -897,155 +897,164 @@ PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, co
|
|||
const db::Shapes &shapes = cell.shapes (*l);
|
||||
std::vector <EdgeWithIndex> edge_sel;
|
||||
|
||||
db::ShapeIterator shape = shapes.begin_touching (scan_box, flags (), prop_sel (), inv_prop_sel ());
|
||||
while (! shape.at_end ()) {
|
||||
// two passes - one with points, second with edges
|
||||
|
||||
bool match = false;
|
||||
double d = std::numeric_limits<double>::max ();
|
||||
bool any = false;
|
||||
for (int pass = 0; pass < 2 && ! any; ++pass) {
|
||||
|
||||
edge_sel.clear ();
|
||||
db::ShapeIterator shape = shapes.begin_touching (scan_box, flags (), prop_sel (), inv_prop_sel ());
|
||||
while (! shape.at_end ()) {
|
||||
|
||||
checkpoint ();
|
||||
bool match = false;
|
||||
double d = std::numeric_limits<double>::max ();
|
||||
|
||||
// in point mode, test the edges and use a "closest" criterion
|
||||
if (shape->is_polygon ()) {
|
||||
edge_sel.clear ();
|
||||
|
||||
for (unsigned int c = 0; c < shape->holes () + 1; ++c) {
|
||||
checkpoint ();
|
||||
|
||||
// in point mode, test the edges and use a "closest" criterion
|
||||
if (shape->is_polygon ()) {
|
||||
|
||||
for (unsigned int c = 0; c < shape->holes () + 1; ++c) {
|
||||
|
||||
unsigned int n = 0;
|
||||
db::Shape::polygon_edge_iterator ee;
|
||||
for (db::Shape::polygon_edge_iterator e = shape->begin_edge (c); ! e.at_end (); e = ee, ++n) {
|
||||
|
||||
ee = e;
|
||||
++ee;
|
||||
unsigned int nn = ee.at_end () ? 0 : n + 1;
|
||||
|
||||
unsigned int r = test_edge (t, *e, pass == 0, d, match);
|
||||
if (r) {
|
||||
edge_sel.clear ();
|
||||
if ((r & 1) != 0) {
|
||||
edge_sel.push_back (EdgeWithIndex (db::Edge ((*e).p1 (), (*e).p1 ()), n, n, c));
|
||||
}
|
||||
if ((r & 2) != 0) {
|
||||
edge_sel.push_back (EdgeWithIndex (db::Edge ((*e).p2 (), (*e).p2 ()), nn, nn, c));
|
||||
}
|
||||
if (r == 3) {
|
||||
edge_sel.push_back (EdgeWithIndex (*e, n, nn, c));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else if (shape->is_path ()) {
|
||||
|
||||
// test the "spine"
|
||||
db::Shape::point_iterator pt = shape->begin_point ();
|
||||
if (pt != shape->end_point ()) {
|
||||
db::Point p (*pt);
|
||||
++pt;
|
||||
unsigned int n = 0;
|
||||
for (; pt != shape->end_point (); ++pt, ++n) {
|
||||
unsigned int r = test_edge (t, db::Edge (p, *pt), pass == 0, d, match);
|
||||
if (r) {
|
||||
edge_sel.clear ();
|
||||
if ((r & 1) != 0) {
|
||||
edge_sel.push_back (EdgeWithIndex (db::Edge (p, p), n, n, 0));
|
||||
}
|
||||
if ((r & 2) != 0) {
|
||||
edge_sel.push_back (EdgeWithIndex (db::Edge (*pt, *pt), n + 1, n + 1, 0));
|
||||
}
|
||||
if (r == 3) {
|
||||
edge_sel.push_back (EdgeWithIndex (db::Edge (p, *pt), n, n + 1, 0));
|
||||
}
|
||||
}
|
||||
p = *pt;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (shape->is_box ()) {
|
||||
|
||||
const db::Box &box = shape->box ();
|
||||
|
||||
// convert to polygon and test those edges
|
||||
db::Polygon poly (box);
|
||||
|
||||
unsigned int n = 0;
|
||||
db::Shape::polygon_edge_iterator ee;
|
||||
for (db::Shape::polygon_edge_iterator e = 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;
|
||||
|
||||
unsigned int r = test_edge (t, *e, d, match);
|
||||
unsigned int r = test_edge (t, *e, pass == 0, d, match);
|
||||
if (r) {
|
||||
edge_sel.clear ();
|
||||
if ((r & 1) == 1) {
|
||||
edge_sel.push_back (EdgeWithIndex (db::Edge ((*e).p1 (), (*e).p1 ()), n, n, c));
|
||||
if ((r & 1) != 0) {
|
||||
edge_sel.push_back (EdgeWithIndex (db::Edge ((*e).p1 (), (*e).p1 ()), n, n, 0));
|
||||
}
|
||||
if ((r & 2) == 2) {
|
||||
edge_sel.push_back (EdgeWithIndex (db::Edge ((*e).p2 (), (*e).p2 ()), nn, nn, c));
|
||||
if ((r & 2) != 0) {
|
||||
edge_sel.push_back (EdgeWithIndex (db::Edge ((*e).p2 (), (*e).p2 ()), nn, nn, 0));
|
||||
}
|
||||
if (r == 3) {
|
||||
edge_sel.push_back (EdgeWithIndex (*e, n, nn, c));
|
||||
edge_sel.push_back (EdgeWithIndex (*e, n, nn, 0));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
} else if (shape->is_text ()) {
|
||||
|
||||
} else if (shape->is_path ()) {
|
||||
db::Point tp (shape->text_trans () * db::Point ());
|
||||
|
||||
// test the "spine"
|
||||
db::Shape::point_iterator pt = shape->begin_point ();
|
||||
if (pt != shape->end_point ()) {
|
||||
db::Point p (*pt);
|
||||
++pt;
|
||||
unsigned int n = 0;
|
||||
for (; pt != shape->end_point (); ++pt, ++n) {
|
||||
unsigned int r = test_edge (t, db::Edge (p, *pt), d, match);
|
||||
if (r) {
|
||||
if (text_info ()) {
|
||||
|
||||
db::CplxTrans t_dbu = db::CplxTrans (layout ().dbu ()) * t;
|
||||
db::Text text;
|
||||
shape->text (text);
|
||||
db::Box tb (t_dbu.inverted () * text_info ()->bbox (t_dbu * text, vp));
|
||||
if (tb.contains (hit_box.center ())) {
|
||||
d = tp.distance (hit_box.center ());
|
||||
edge_sel.clear ();
|
||||
if ((r & 1) == 1) {
|
||||
edge_sel.push_back (EdgeWithIndex (db::Edge (p, p), n, n, 0));
|
||||
}
|
||||
if ((r & 2) == 2) {
|
||||
edge_sel.push_back (EdgeWithIndex (db::Edge (*pt, *pt), n + 1, n + 1, 0));
|
||||
}
|
||||
if (r == 3) {
|
||||
edge_sel.push_back (EdgeWithIndex (db::Edge (p, *pt), n, n + 1, 0));
|
||||
}
|
||||
edge_sel.push_back (EdgeWithIndex (db::Edge (tp, tp), 0, 0, 0));
|
||||
match = true;
|
||||
}
|
||||
p = *pt;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (shape->is_box ()) {
|
||||
} else {
|
||||
|
||||
const db::Box &box = shape->box ();
|
||||
|
||||
// convert to polygon and test those edges
|
||||
db::Polygon poly (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;
|
||||
|
||||
unsigned int r = test_edge (t, *e, d, match);
|
||||
if (r) {
|
||||
edge_sel.clear ();
|
||||
if ((r & 1) == 1) {
|
||||
edge_sel.push_back (EdgeWithIndex (db::Edge ((*e).p1 (), (*e).p1 ()), n, n, 0));
|
||||
}
|
||||
if ((r & 2) == 2) {
|
||||
edge_sel.push_back (EdgeWithIndex (db::Edge ((*e).p2 (), (*e).p2 ()), nn, nn, 0));
|
||||
}
|
||||
if (r == 3) {
|
||||
edge_sel.push_back (EdgeWithIndex (*e, n, nn, 0));
|
||||
if (hit_box.contains (tp)) {
|
||||
d = tp.distance (hit_box.center ());
|
||||
edge_sel.clear ();
|
||||
edge_sel.push_back (EdgeWithIndex (db::Edge (tp, tp), 0, 0, 0));
|
||||
match = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else if (shape->is_text ()) {
|
||||
if (match && closer (d)) {
|
||||
|
||||
db::Point tp (shape->text_trans () * db::Point ());
|
||||
|
||||
if (text_info ()) {
|
||||
|
||||
db::CplxTrans t_dbu = db::CplxTrans (layout ().dbu ()) * t;
|
||||
db::Text text;
|
||||
shape->text (text);
|
||||
db::Box tb (t_dbu.inverted () * text_info ()->bbox (t_dbu * text, vp));
|
||||
if (tb.contains (hit_box.center ())) {
|
||||
d = tp.distance (hit_box.center ());
|
||||
edge_sel.clear ();
|
||||
edge_sel.push_back (EdgeWithIndex (db::Edge (tp, tp), 0, 0, 0));
|
||||
match = true;
|
||||
// in point mode just store that found that has the least "distance"
|
||||
if (m_founds.empty ()) {
|
||||
m_founds.push_back (founds_vector_type::value_type ());
|
||||
}
|
||||
|
||||
} else {
|
||||
lay::ObjectInstPath &inst_path = m_founds.back ().first;
|
||||
|
||||
if (hit_box.contains (tp)) {
|
||||
d = tp.distance (hit_box.center ());
|
||||
edge_sel.clear ();
|
||||
edge_sel.push_back (EdgeWithIndex (db::Edge (tp, tp), 0, 0, 0));
|
||||
match = true;
|
||||
}
|
||||
inst_path.set_cv_index (cv_index ());
|
||||
inst_path.set_topcell (topcell ());
|
||||
inst_path.assign_path (path ().begin (), path ().end ());
|
||||
inst_path.set_layer (*l);
|
||||
inst_path.set_shape (*shape);
|
||||
|
||||
m_founds.back ().second = edge_sel;
|
||||
|
||||
any = true;
|
||||
|
||||
}
|
||||
|
||||
++shape;
|
||||
|
||||
}
|
||||
|
||||
if (match && closer (d)) {
|
||||
|
||||
// in point mode just store that found that has the least "distance"
|
||||
if (m_founds.empty ()) {
|
||||
m_founds.push_back (founds_vector_type::value_type ());
|
||||
}
|
||||
|
||||
lay::ObjectInstPath &inst_path = m_founds.back ().first;
|
||||
|
||||
inst_path.set_cv_index (cv_index ());
|
||||
inst_path.set_topcell (topcell ());
|
||||
inst_path.assign_path (path ().begin (), path ().end ());
|
||||
inst_path.set_layer (*l);
|
||||
inst_path.set_shape (*shape);
|
||||
|
||||
m_founds.back ().second = edge_sel;
|
||||
|
||||
}
|
||||
|
||||
++shape;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1153,6 +1162,7 @@ PartialService::timeout ()
|
|||
m_hover = true;
|
||||
|
||||
mp_view->clear_transient_selection ();
|
||||
clear_mouse_cursors ();
|
||||
|
||||
// compute search box
|
||||
double l = catch_distance ();
|
||||
|
|
@ -2413,6 +2423,10 @@ PartialService::enter_edge (const EdgeWithIndex &e, size_t &nmarker, partial_obj
|
|||
db::DEdge ee = db::DEdge (db::DPoint (ep2) + ((db::DPoint (ep1) - db::DPoint (ep2)) * 0.25), db::DPoint (ep2));
|
||||
marker->set (ee, db::DCplxTrans (gt), tv);
|
||||
|
||||
if (transient && sel->second.size () == 1) {
|
||||
add_mouse_cursor (ep2, sel->first.cv_index (), gt, tv, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (p1_sel && !p12_sel) {
|
||||
|
|
@ -2423,12 +2437,22 @@ PartialService::enter_edge (const EdgeWithIndex &e, size_t &nmarker, partial_obj
|
|||
db::DEdge ee = db::DEdge (db::DPoint (ep1), db::DPoint (ep1) + ((db::DPoint (ep2) - db::DPoint (ep1)) * 0.25));
|
||||
marker->set (ee, db::DCplxTrans (gt), tv);
|
||||
|
||||
}
|
||||
if (transient && sel->second.size () == 1) {
|
||||
add_mouse_cursor (ep1, sel->first.cv_index (), gt, tv, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (p12_sel) {
|
||||
|
||||
lay::Marker *marker = new_marker (nmarker, sel->first.cv_index (), transient);
|
||||
marker->set_vertex_size (0);
|
||||
marker->set (enew, gt, tv);
|
||||
|
||||
if (transient) {
|
||||
add_edge_marker (enew, sel->first.cv_index (), gt, tv, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -210,6 +210,7 @@ EditorServiceBase::EditorServiceBase (LayoutViewBase *view)
|
|||
: lay::ViewService (view->canvas ()),
|
||||
lay::Editable (view),
|
||||
lay::Plugin (view),
|
||||
mp_view (view),
|
||||
m_cursor_enabled (true),
|
||||
m_has_tracking_position (false)
|
||||
{
|
||||
|
|
@ -229,12 +230,30 @@ EditorServiceBase::add_mouse_cursor (const db::DPoint &pt, bool emphasize)
|
|||
m_mouse_cursor_markers.push_back (new MouseCursorViewObject (this, ui (), pt, emphasize));
|
||||
}
|
||||
|
||||
void
|
||||
EditorServiceBase::add_mouse_cursor (const db::Point &pt, unsigned int cv_index, const db::ICplxTrans >, const std::vector<db::DCplxTrans> &tv, bool emphasize)
|
||||
{
|
||||
double dbu = mp_view->cellview (cv_index)->layout ().dbu ();
|
||||
for (auto t = tv.begin (); t != tv.end (); ++t) {
|
||||
add_mouse_cursor (*t * db::CplxTrans (dbu) * gt * pt, emphasize);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EditorServiceBase::add_edge_marker (const db::DEdge &e, bool emphasize)
|
||||
{
|
||||
m_mouse_cursor_markers.push_back (new EdgeMarkerViewObject (this, ui (), e, emphasize));
|
||||
}
|
||||
|
||||
void
|
||||
EditorServiceBase::add_edge_marker (const db::Edge &e, unsigned int cv_index, const db::ICplxTrans >, const std::vector<db::DCplxTrans> &tv, bool emphasize)
|
||||
{
|
||||
double dbu = mp_view->cellview (cv_index)->layout ().dbu ();
|
||||
for (auto t = tv.begin (); t != tv.end (); ++t) {
|
||||
add_edge_marker (*t * db::CplxTrans (dbu) * gt * e, emphasize);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EditorServiceBase::clear_mouse_cursors ()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -74,10 +74,20 @@ public:
|
|||
*/
|
||||
void add_mouse_cursor (const db::DPoint &pt, bool emphasize = false);
|
||||
|
||||
/**
|
||||
* @brief Adds a mouse cursor to the given point in layout space
|
||||
*/
|
||||
void add_mouse_cursor (const db::Point &pt, unsigned int cv_index, const db::ICplxTrans >, const std::vector<db::DCplxTrans> &tv, bool emphasize = false);
|
||||
|
||||
/**
|
||||
* @brief Adds an edge marker for the given edge
|
||||
*/
|
||||
void add_edge_marker (const db::DEdge &e, bool emphasize);
|
||||
void add_edge_marker (const db::DEdge &e, bool emphasize = false);
|
||||
|
||||
/**
|
||||
* @brief Adds an edge marker for the given edge in layout space
|
||||
*/
|
||||
void add_edge_marker (const db::Edge &e, unsigned int cv_index, const db::ICplxTrans >, const std::vector<db::DCplxTrans> &tv, bool emphasize = false);
|
||||
|
||||
/**
|
||||
* @brief Resets the mouse cursor
|
||||
|
|
@ -132,6 +142,7 @@ protected:
|
|||
|
||||
private:
|
||||
// The marker representing the mouse cursor
|
||||
lay::LayoutViewBase *mp_view;
|
||||
std::vector<lay::ViewObject *> m_mouse_cursor_markers;
|
||||
tl::Color m_cursor_color;
|
||||
bool m_cursor_enabled;
|
||||
|
|
|
|||
|
|
@ -123,47 +123,74 @@ Finder::start (lay::LayoutViewBase *view, unsigned int cv_index, const std::vect
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
Finder::test_edge (const db::ICplxTrans &trans, const db::Edge &edge, double &distance, bool &match)
|
||||
{
|
||||
if (test_edge (trans, edge, true, distance, match) == 0) {
|
||||
test_edge (trans, edge, false, distance, match);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int
|
||||
Finder::test_edge (const db::ICplxTrans &trans, const db::Edge &edg, double &distance, bool &match)
|
||||
Finder::test_edge (const db::ICplxTrans &trans, const db::Edge &edg, bool points, double &distance, bool &match)
|
||||
{
|
||||
db::Point p1 = trans * edg.p1 ();
|
||||
db::Point p2 = trans * edg.p2 ();
|
||||
|
||||
unsigned int ret = 0;
|
||||
|
||||
// we hit the region with the edge end points - take the closest vertex
|
||||
if (m_region.contains (p1) || m_region.contains (p2)) {
|
||||
if (points) {
|
||||
|
||||
double d1 = p1.double_distance (m_region.center ());
|
||||
double d2 = p2.double_distance (m_region.center ());
|
||||
// we hit the region with the edge end points - take the closest vertex
|
||||
if (m_region.contains (p1) || m_region.contains (p2)) {
|
||||
|
||||
double d1 = p1.double_distance (m_region.center ());
|
||||
double d2 = p2.double_distance (m_region.center ());
|
||||
if (d1 < d2) {
|
||||
ret = 1;
|
||||
} else {
|
||||
ret = 2;
|
||||
}
|
||||
|
||||
double d = std::min (d1, d2);
|
||||
// add a penalty of 1 DBU for being on the wrong
|
||||
// side of the edge - this favors the right edge
|
||||
// in case of butting corners
|
||||
if (ret == 1) {
|
||||
if (db::sprod_sign (m_region.center () - p1, p2 - p1) < 0) {
|
||||
d += trans.ctrans (1);
|
||||
}
|
||||
} else {
|
||||
if (db::sprod_sign (m_region.center () - p2, p1 - p2) < 0) {
|
||||
d += trans.ctrans (1);
|
||||
}
|
||||
}
|
||||
|
||||
if (! match || d < distance) {
|
||||
distance = d;
|
||||
}
|
||||
|
||||
match = true;
|
||||
|
||||
double d = std::min (d1, d2);
|
||||
if (! match || d < distance) {
|
||||
distance = d;
|
||||
}
|
||||
|
||||
if (d1 < d2) {
|
||||
ret = 1;
|
||||
} else {
|
||||
ret = 2;
|
||||
}
|
||||
|
||||
match = true;
|
||||
|
||||
}
|
||||
} else {
|
||||
|
||||
// if the edge cuts through the active region: test the
|
||||
// edge as a whole
|
||||
if (ret == 0) {
|
||||
// if the edge cuts through the active region: test the
|
||||
// edge as a whole
|
||||
db::Edge edg_trans (p1, p2);
|
||||
if (edg_trans.clipped (m_region).first) {
|
||||
|
||||
double d = edg_trans.distance_abs (m_region.center ());
|
||||
if (! match || d < distance) {
|
||||
distance = d;
|
||||
ret = 3;
|
||||
}
|
||||
|
||||
ret = 3;
|
||||
match = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -176,11 +176,18 @@ protected:
|
|||
*
|
||||
* "trans" is the transformation to be applied to the edge before the test.
|
||||
*
|
||||
* If "points" is true, only points are tested, otherwise edges are tested.
|
||||
*
|
||||
* This method returns a mask indicating which point of the edge was matching.
|
||||
* Bit 0 of this mask indicates the first point is matching, bit 1 indicates the
|
||||
* second point is matching.
|
||||
*/
|
||||
unsigned int test_edge (const db::ICplxTrans &trans, const db::Edge &edge, double &distance, bool &match);
|
||||
unsigned int test_edge (const db::ICplxTrans &trans, const db::Edge &edge, bool points, double &distance, bool &match);
|
||||
|
||||
/**
|
||||
* @brief Tests an edge in point mode and edge mode (later)
|
||||
*/
|
||||
void test_edge (const db::ICplxTrans &trans, const db::Edge &edge, double &distance, bool &match);
|
||||
|
||||
private:
|
||||
void do_find (const db::Cell &cell, int level, const db::DCplxTrans &vp, const db::ICplxTrans &t);
|
||||
|
|
|
|||
Loading…
Reference in New Issue