WIP: rulers and auto measure

* Fixed snapping - don't use double objects with micron units
* Implemented first version of auto measure - needs improvement.
This commit is contained in:
Matthias Koefferlein 2017-06-30 00:03:21 +02:00
parent 919de1af01
commit c263878a4b
3 changed files with 318 additions and 89 deletions

View File

@ -1389,25 +1389,62 @@ Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio
const ant::Template &tpl = current_template ();
m_p1 = snap1 (p, m_obj_snap && tpl.snap ()).second;
// create and start dragging the ruler
if (tpl.mode () == ant::Template::RulerSingleClick) {
db::DPoint pt = snap1 (p, m_obj_snap && tpl.snap ()).second;
// begin the transaction
tl_assert (! manager ()->transacting ());
manager ()->transaction (tl::to_string (QObject::tr ("Create ruler")));
m_current = ant::Object (pt, pt, 0, tpl);
show_message ();
insert_ruler (ant::Object (m_p1, m_p1, 0, tpl), true);
insert_ruler (m_current, true);
// end the transaction
manager ()->commit ();
} else if (tpl.mode () == ant::Template::RulerAutoMetric) {
// for auto-metric we need some cutline constraint - any or global won't do.
lay::angle_constraint_type ac = tpl.angle_constraint ();
ac = (ac == lay::AC_Global ? m_snap_mode : ac);
if (ac == lay::AC_Any || ac == lay::AC_Global) {
ac = lay::AC_Diagonal;
}
db::DVector g;
if (m_grid_snap) {
g = db::DVector (m_grid, m_grid);
}
double snap_range = widget ()->mouse_event_trans ().inverted ().ctrans (m_snap_range);
snap_range *= 0.5;
std::pair<bool, db::DEdge> ee = lay::obj_snap2 (mp_view, p, p, g, ac, snap_range, snap_range * 1000.0);
if (ee.first) {
// begin the transaction
tl_assert (! manager ()->transacting ());
manager ()->transaction (tl::to_string (QObject::tr ("Create ruler")));
m_current = ant::Object (ee.second.p1 (), ee.second.p2 (), 0, tpl);
show_message ();
insert_ruler (m_current, true);
// end the transaction
manager ()->commit ();
}
} else {
m_p1 = snap1 (p, m_obj_snap && tpl.snap ()).second;
m_current = ant::Object (m_p1, m_p1, 0, tpl);
show_message ();

View File

@ -25,6 +25,8 @@
#include "laySnap.h"
#include "layLayoutView.h"
#include "dbEdge.h"
namespace lay
{
@ -223,12 +225,14 @@ snap_angle (const db::DVector &in, lay::angle_constraint_type ac)
class ContourFinder
{
public:
ContourFinder (const db::DPoint &original, const db::DVector &grid, const std::vector <db::DEdge> &cutlines)
ContourFinder (const db::DPoint &original, const db::DVector &grid, const std::vector <db::DEdge> &cutlines, bool directed = false)
: m_any (false), m_any_exact (false),
m_original (original), m_tests (10000 /* max. number of tests, TODO: make variable? */),
mp_layout (0), m_cutlines (cutlines), mp_prop_sel (0), m_inv_prop_sel (false)
m_original (original), m_is_vertex (false), m_is_vertex_exact (false),
m_tests (10000 /* max. number of tests, TODO: make variable? */),
mp_layout (0), m_cutlines (cutlines), mp_prop_sel (0), m_inv_prop_sel (false),
m_directed (directed)
{
m_projection_constraint = ! m_cutlines.empty ();
m_projection_constraint = ! m_cutlines.empty () && ! directed;
// Add synthetic cutlines for the grid snap along edges
if (m_cutlines.empty () && grid != db::DVector ()) {
@ -250,7 +254,7 @@ public:
find (lay::LayoutView *view, int cv_index, const std::vector<db::DCplxTrans> &trans, const db::Layout &layout, const db::DBox &region, const db::Cell &cell, unsigned int l,
const std::set<db::properties_id_type> *prop_sel, bool inv_prop_sel, int min_hier_level, int max_hier_level)
{
m_region = region;
m_region = region * (1.0 / layout.dbu ());
mp_layout = &layout;
mp_prop_sel = prop_sel;
m_inv_prop_sel = inv_prop_sel;
@ -258,16 +262,60 @@ public:
const lay::CellView &cv = view->cellview (cv_index);
for (std::vector<db::DCplxTrans>::const_iterator t = trans.begin (); t != trans.end () && m_tests > 0; ++t) {
do_find (view, cv_index, cell, l, min_hier_level, max_hier_level, *t * db::CplxTrans (cv->layout ().dbu ()) * cv.context_trans ());
do_find (view, cv_index, cell, l, min_hier_level, max_hier_level, *t * db::CplxTrans () * cv.context_trans ());
}
}
const db::DPoint &get_found () const
bool is_vertex () const
{
if (m_any_exact) {
return m_closest_exact;
return m_is_vertex_exact;
} else if (m_any) {
return m_closest;
return m_is_vertex;
} else {
return false;
}
}
std::pair<db::DEdge, db::DEdge> get_found_vertex_edges () const
{
if (m_any_exact) {
return std::make_pair (m_edge1_exact * mp_layout->dbu (), m_edge2_exact * mp_layout->dbu ());
} else if (m_any) {
return std::make_pair (m_edge1 * mp_layout->dbu (), m_edge2 * mp_layout->dbu ());
} else {
return std::pair<db::DEdge, db::DEdge> ();
}
}
bool has_found_edge () const
{
if (m_any_exact) {
return ! m_edge1_exact.is_degenerate ();
} else if (m_any) {
return ! m_edge1.is_degenerate ();
} else {
return false;
}
}
db::DEdge get_found_edge () const
{
if (m_any_exact) {
return m_edge1_exact * mp_layout->dbu ();
} else if (m_any) {
return m_edge1 * mp_layout->dbu ();
} else {
return db::DEdge ();
}
}
db::DPoint get_found () const
{
if (m_any_exact) {
return m_closest_exact * mp_layout->dbu ();
} else if (m_any) {
return m_closest * mp_layout->dbu ();
} else {
return m_original;
}
@ -275,30 +323,54 @@ public:
bool any () const
{
// It looks like it's better to deliver any value than nothing ..
#if 0
return (m_any || m_any_exact) && m_tests > 0;
#else
return (m_any || m_any_exact);
#endif
}
bool any_exact () const
{
return m_any_exact;
}
private:
void
find_closest_exact (const db::DPoint &p)
find_closest_exact (const db::DPoint &p, const db::DEdge &e)
{
if (! m_any_exact || m_original.distance (p) < m_original.distance (m_closest_exact)) {
if (! m_any_exact || m_original.distance (p * mp_layout->dbu ()) < m_original.distance (m_closest_exact * mp_layout->dbu ())) {
if (m_directed) {
for (std::vector<db::DEdge>::const_iterator cl = m_cutlines.begin (); cl != m_cutlines.end (); ++cl) {
if (db::sprod_sign (p - cl->p1 () * (1.0 / mp_layout->dbu ()), cl->d () * (1.0 / mp_layout->dbu ())) <= 0) {
return;
}
}
}
m_edge1_exact = m_edge2_exact = e;
m_is_vertex_exact = false;
m_closest_exact = p;
m_any_exact = true;
}
}
void
find_closest (const db::DPoint &p)
find_closest (const db::DPoint &p, const db::DEdge &e)
{
if (! m_any || m_original.distance (p) < m_original.distance (m_closest)) {
if (! m_any || m_original.distance (p * mp_layout->dbu ()) < m_original.distance (m_closest * mp_layout->dbu ())) {
if (m_directed) {
for (std::vector<db::DEdge>::const_iterator cl = m_cutlines.begin (); cl != m_cutlines.end (); ++cl) {
if (db::sprod_sign (p - cl->p1 () * (1.0 / mp_layout->dbu ()), cl->d () * (1.0 / mp_layout->dbu ())) < 0) {
return;
}
}
}
m_edge1 = m_edge2 = e;
m_is_vertex = false;
m_closest = p;
m_any = true;
}
}
@ -306,7 +378,7 @@ private:
{
if (! m_projection_constraint) {
find_closest_exact (p);
find_closest_exact (p, db::DEdge (p, p));
} else {
@ -315,13 +387,13 @@ private:
// test point.
for (std::vector <db::DEdge>::const_iterator cl = m_cutlines.begin (); cl != m_cutlines.end (); ++cl) {
std::pair<bool, db::DPoint> ret;
ret = db::DEdge (p, p + db::DVector (1.0, 0.0)).cut_point (*cl);
ret = db::DEdge (p, p + db::DVector (1.0, 0.0)).cut_point (*cl * (1.0 / mp_layout->dbu ()));
if (ret.first) {
find_closest_exact (ret.second);
find_closest_exact (ret.second, db::DEdge (p, p));
}
ret = db::DEdge (p, p + db::DVector (0.0, 1.0)).cut_point (*cl);
ret = db::DEdge (p, p + db::DVector (0.0, 1.0)).cut_point (*cl * (1.0 / mp_layout->dbu ()));
if (ret.first) {
find_closest_exact (ret.second);
find_closest_exact (ret.second, db::DEdge (p, p));
}
}
@ -335,16 +407,16 @@ private:
// do the checks in dbu space rather than micron space because
// the tolerances are set up for this.
for (std::vector <db::DEdge>::const_iterator cl = m_cutlines.begin (); cl != m_cutlines.end (); ++cl) {
std::pair<bool, db::DPoint> ret = e.cut_point (*cl);
std::pair<bool, db::DPoint> ret = e.cut_point (*cl * (1.0 / mp_layout->dbu ()));
if (ret.first) {
// if the projection exactly hits the edge and the point
// of crossing is inside the search region, take this as
// the exact hit. Exact hits have priority over projected
// hits.
if (e.contains (ret.second) && m_region.contains (ret.second)) {
find_closest_exact (ret.second);
find_closest_exact (ret.second, e);
} else {
find_closest (ret.second);
find_closest (ret.second, e);
}
any_point = true;
}
@ -361,26 +433,43 @@ private:
db::DVector n (-v.y (), v.x ());
double f = d / n.double_length ();
db::DPoint e1 (m_original.x () - n.x () * f,
m_original.y () - n.y () * f);
db::DPoint e2 (m_original.x () + n.x () * f,
m_original.y () + n.y () * f);
db::DPoint e1 (m_original.x () * (1.0 / mp_layout->dbu ()) - n.x () * f,
m_original.y () * (1.0 / mp_layout->dbu ()) - n.y () * f);
db::DPoint e2 (m_original.x () * (1.0 / mp_layout->dbu ()) + n.x () * f,
m_original.y () * (1.0 / mp_layout->dbu ()) + n.y () * f);
double dmin = mp_layout->dbu () * std::numeric_limits <db::Coord>::min ();
double dmax = mp_layout->dbu () * std::numeric_limits <db::Coord>::max ();
double dmin = std::numeric_limits <db::Coord>::min ();
double dmax = std::numeric_limits <db::Coord>::max ();
db::DBox dworld (dmin, dmin, dmax, dmax);
if (dworld.contains (e1) && dworld.contains (e2)) {
std::pair<bool, db::DPoint> ip = e.intersect_point (db::DEdge (e1, e2));
if (ip.first) {
find_closest (ip.second);
find_closest (ip.second, e);
}
}
}
if (m_any && m_closest.equal (e.p1 ())) {
m_edge1 = e;
m_is_vertex = true;
}
if (m_any && m_closest.equal (e.p2 ())) {
m_edge2 = e;
m_is_vertex = true;
}
if (m_any_exact && m_closest_exact.equal (e.p1 ())) {
m_edge1_exact = e;
m_is_vertex_exact = true;
}
if (m_any_exact && m_closest_exact.equal (e.p2 ())) {
m_edge2_exact = e;
m_is_vertex_exact = true;
}
}
void
@ -508,6 +597,9 @@ private:
bool m_any, m_any_exact;
db::DPoint m_closest, m_closest_exact;
db::DPoint m_original;
db::DEdge m_edge1, m_edge2;
db::DEdge m_edge1_exact, m_edge2_exact;
bool m_is_vertex, m_is_vertex_exact;
int m_tests;
const db::Layout *mp_layout;
db::DBox m_region;
@ -515,62 +607,68 @@ private:
const std::set<db::properties_id_type> *mp_prop_sel;
bool m_inv_prop_sel;
bool m_projection_constraint;
bool m_directed;
};
static
void run_finder (ContourFinder &finder, lay::LayoutView *view, const db::DPoint &dp, double snap_range)
{
if (! view) {
return;
}
for (lay::LayerPropertiesConstIterator l = view->begin_layers (); ! l.at_end (); ++l) {
if (l->is_visual ()) {
const lay::CellView &cv = view->cellview (l->cellview_index ());
if (cv.is_valid ()) {
double px = dp.x ();
double py = dp.y ();
double dd = std::max (0.0, snap_range);
double dmin = cv->layout ().dbu () * std::numeric_limits <db::Coord>::min ();
double dmax = cv->layout ().dbu () * std::numeric_limits <db::Coord>::max ();
db::DBox dworld (dmin, dmin, dmax, dmax);
db::DBox dregion = dworld & db::DBox (px - dd, py - dd, px + dd, py + dd);
const lay::CellView &cv = view->cellview (l->cellview_index ());
int ctx_levels = cv.specific_path ().size ();
int min_hier_level = view->get_min_hier_levels () - ctx_levels;
int max_hier_level = view->get_max_hier_levels () - ctx_levels;
if (l->hier_levels ().has_from_level ()) {
min_hier_level = l->hier_levels ().from_level (ctx_levels, min_hier_level);
}
if (l->hier_levels ().has_to_level ()) {
max_hier_level = l->hier_levels ().to_level (ctx_levels, max_hier_level);
}
if (! dregion.empty ()) {
finder.find (view, l->cellview_index (), l->trans (), cv->layout (),
db::DBox (dregion.p1 (), dregion.p2 ()),
*cv.cell (),
l->layer_index (),
&l->prop_sel (), l->inverse_prop_sel (),
min_hier_level, max_hier_level);
}
}
}
}
}
std::pair <bool, db::DPoint>
obj_snap (lay::LayoutView *view, const db::DPoint &pt, const db::DVector &grid, double snap_range, const std::vector <db::DEdge> &cutlines)
{
db::DPoint dp (pt);
ContourFinder finder (dp, grid, cutlines);
if (view) {
for (lay::LayerPropertiesConstIterator l = view->begin_layers (); ! l.at_end (); ++l) {
if (l->is_visual ()) {
const lay::CellView &cv = view->cellview (l->cellview_index ());
if (cv.is_valid ()) {
double px = dp.x ();
double py = dp.y ();
double dd = std::max (0.0, snap_range);
double dmin = cv->layout ().dbu () * std::numeric_limits <db::Coord>::min ();
double dmax = cv->layout ().dbu () * std::numeric_limits <db::Coord>::max ();
db::DBox dworld (dmin, dmin, dmax, dmax);
db::DBox dregion = dworld & db::DBox (px - dd, py - dd, px + dd, py + dd);
const lay::CellView &cv = view->cellview (l->cellview_index ());
int ctx_levels = cv.specific_path ().size ();
int min_hier_level = view->get_min_hier_levels () - ctx_levels;
int max_hier_level = view->get_max_hier_levels () - ctx_levels;
if (l->hier_levels ().has_from_level ()) {
min_hier_level = l->hier_levels ().from_level (ctx_levels, min_hier_level);
}
if (l->hier_levels ().has_to_level ()) {
max_hier_level = l->hier_levels ().to_level (ctx_levels, max_hier_level);
}
if (! dregion.empty ()) {
finder.find (view, l->cellview_index (), l->trans (), cv->layout (),
db::DBox (dregion.p1 (), dregion.p2 ()),
*cv.cell (),
l->layer_index (),
&l->prop_sel (), l->inverse_prop_sel (),
min_hier_level, max_hier_level);
}
}
}
}
}
run_finder (finder, view, dp, snap_range);
// in grid snap mode, for the "object free" analysis snap to the grid now
if (grid != db::DVector ()) {
@ -593,7 +691,7 @@ obj_snap (lay::LayoutView *view, const db::DPoint &pt, const db::DVector &grid,
if (finder.any () && anyp) {
// if both the projection and the finder are sucessful, decide by a heuristic criterion which to take
// (the projection gets a penalty (of the snap range) a counts less than the finder's choice)
// (the projection gets a penalty (of the snap range) to make it count less than the finder's choice)
// This avoids extreme distortions of the ruler due to projection on long edges.
if ((dp.distance (closest) + snap_range) * 5.0 < dp.distance (finder.get_found ())) {
return std::make_pair (false, closest);
@ -609,11 +707,77 @@ obj_snap (lay::LayoutView *view, const db::DPoint &pt, const db::DVector &grid,
}
}
std::pair <bool, db::DPoint>
obj_snap (lay::LayoutView *view, const db::DPoint &p1, const db::DPoint &p2, const db::DVector &grid, lay::angle_constraint_type snap_mode, double snap_range)
std::pair <bool, db::DEdge>
obj_snap2 (lay::LayoutView *view, const db::DPoint &pt, const db::DVector &grid, double min_search_range, double max_search_range, const std::vector <db::DEdge> &cutlines)
{
std::vector <db::DEdge> cutlines;
db::DPoint dp (pt);
ContourFinder finder (dp, grid, cutlines);
double sr = min_search_range;
while (sr < max_search_range + 1e-6) {
run_finder (finder, view, dp, sr);
if (finder.any ()) {
db::DPoint p1 = finder.get_found ();
std::vector <db::DEdge> cl;
db::DVector n;
if (finder.is_vertex () || finder.has_found_edge ()) {
if (finder.is_vertex ()) {
std::pair<db::DEdge, db::DEdge> ee = finder.get_found_vertex_edges ();
db::DVector d1 = ee.first.d ();
if (d1.double_length () > 1e-6) {
d1 *= (1.0 / d1.double_length ());
}
db::DVector d2 = ee.second.d ();
if (d2.double_length () > 1e-6) {
d2 *= (1.0 / d1.double_length ());
}
n = ((d1 + d2) * 0.5).transformed (db::DTrans (db::DTrans::r90));
} else {
n = finder.get_found_edge ().d ().transformed (db::DTrans (db::DTrans::r90));
}
if (db::sprod (n, dp - p1) < 0.0) {
n = -n;
}
cl.push_back (db::DEdge (p1, p1 + n));
}
ContourFinder finder2 (dp, grid, cl, true /*directional cutlines*/);
double sr2 = min_search_range;
while (sr2 < max_search_range + 1e-6) {
run_finder (finder2, view, dp, sr2);
if (finder2.any_exact ()) {
db::DPoint p2 = finder2.get_found ();
return std::make_pair (true, db::DEdge (p1, p2));
}
sr2 *= 2.0;
}
return std::make_pair (false, db::DEdge ());
}
sr *= 2.0;
}
return std::make_pair (false, db::DEdge ());
}
static void
make_cutlines (lay::angle_constraint_type snap_mode, const db::DPoint &p1, std::vector <db::DEdge> &cutlines)
{
// set up cutlines depending on the mode
if (snap_mode == lay::AC_Ortho) {
cutlines.reserve (2);
@ -629,10 +793,24 @@ obj_snap (lay::LayoutView *view, const db::DPoint &p1, const db::DPoint &p2, con
cutlines.push_back (db::DEdge (p1, p1 + db::DVector (1.0, 0.0)));
cutlines.push_back (db::DEdge (p1, p1 + db::DVector (1.0, 1.0)));
cutlines.push_back (db::DEdge (p1, p1 + db::DVector (1.0, -1.0)));
}
}
}
std::pair <bool, db::DPoint>
obj_snap (lay::LayoutView *view, const db::DPoint &p1, const db::DPoint &p2, const db::DVector &grid, lay::angle_constraint_type snap_mode, double snap_range)
{
std::vector <db::DEdge> cutlines;
make_cutlines (snap_mode, p1, cutlines);
return obj_snap (view, p2, grid, snap_range, cutlines);
}
std::pair <bool, db::DEdge>
obj_snap2 (lay::LayoutView *view, const db::DPoint &p1, const db::DPoint &p2, const db::DVector &grid, lay::angle_constraint_type snap_mode, double min_search_range, double max_search_range)
{
std::vector <db::DEdge> cutlines;
make_cutlines (snap_mode, p1, cutlines);
return obj_snap2 (view, p2, grid, min_search_range, max_search_range, cutlines);
}
}

View File

@ -128,6 +128,13 @@ namespace lay
*/
LAYBASIC_PUBLIC std::pair <bool, db::DPoint> obj_snap (lay::LayoutView *view, const db::DPoint &pt, const db::DVector &grid, double snap_range, const std::vector <db::DEdge> &axes = std::vector <db::DEdge> ());
/**
* @brief Same than obj_snap, but delivers two points on two opposite sides of the initial point
*
* This method basically implements "auto measure"
*/
LAYBASIC_PUBLIC std::pair <bool, db::DEdge> obj_snap2 (lay::LayoutView *view, const db::DPoint &pt, const db::DVector &grid, double min_search_range, double max_search_range, const std::vector <db::DEdge> &axes = std::vector <db::DEdge> ());
/**
* @brief combined grid-, projection- and object snapping provided to implementing "magnetic features"
*
@ -136,6 +143,13 @@ namespace lay
*/
LAYBASIC_PUBLIC std::pair <bool, db::DPoint> obj_snap (lay::LayoutView *view, const db::DPoint &pr, const db::DPoint &pt, const db::DVector &grid, lay::angle_constraint_type ac, double snap_range);
/**
* @brief Same than obj_snap, but delivers two points on two opposite sides of the initial point
*
* This method basically implements "auto measure"
*/
LAYBASIC_PUBLIC std::pair <bool, db::DEdge> obj_snap2 (lay::LayoutView *view, const db::DPoint &pr, const db::DPoint &pt, const db::DVector &grid, lay::angle_constraint_type ac, double min_search_range, double max_search_range);
/**
* @brief Reduce a given vector according to the angle constraint
*/