PDA

View Full Version : Selecting a QLine

Paul Drummond
28th February 2006, 11:33
Sorry if this is (another :o ) stupid question but I am wondering if Qt provides a method for determining if the mouse point is over a Line. I can use QPainterPath::contains() to determine if the mouse is inside a polygon which is nice, but I can't find a way of doing it for a single QLine. Am I missing something obvious or is this something I have to do manually?

wysota
28th February 2006, 13:33
You can use math for that.

Every line is represented by a function y=ax+b and two ending points. Just count if a given point lies on that line ('a' and 'b' can be computed from the ending points and then you just have to check whether the test point doesn't violate this equation).

Paul Drummond
28th February 2006, 13:52
Yeah, I am crap at maths though! Can you expand on that a bit? ;)

It's strange that Qt doesn't provide a method for this as most other graphical elements have a "contains" method for this sort of thing. Is there a specific reason why QLine doesn't have a method?

orb9
28th February 2006, 14:19
bool distancePointLine( const QPoint &p, const QPoint &l1,
const QPoint &l2, float &distance )
{
float lineMag;
float u;
QPoint intersectPnt;

lineMag = magnitude( l2, l1 );

u = ( ( ( p.x() - l1.x() ) * ( l2.x() - l1.x() ) ) +
( ( p.y() - l1.y() ) * ( l2.y() - l1.y() ) ) ) /
( lineMag * lineMag );

if ( u < 0.0f || u > 1.0f )
return false; // closest point does not fall within the line segment

intersectPnt.setX( int( l1.x() + u * ( l2.x() - l1.x() ) ) );
intersectPnt.setY( int( l1.y() + u * ( l2.y() - l1.y() ) ) );

distance = magnitude( p, intersectPnt );

return true;
}

float magnitude( const QPoint &p1, const QPoint &p2 )
{
float vx, vy;

vx = p2.x() - p1.x();
vy = p2.y() - p1.y();

return sqrt( vx * vx + vy * vy );
}

This code is not the nicest, but it works.

Returns true of the point p is on the line segment defined by l1 and l2. The distance contains the distance between the point and the line.

Paul Drummond
1st March 2006, 17:40
Sorry, orb9 but I have tried your code and it doesn't work - I have checked it and rechecked it and examined the code to see whats going on but I can't figure it out.

Basically, the method almost always returns true - sometimes it's false, but I can't determine a pattern based on where I click in the client area!

Paul Drummond
2nd March 2006, 11:09
I have just found QLineF::interesect() which does exactly what I need:

bool Line::hitTest(const QPoint& point) const
{
QPointF intersectPnt;
QLineF line(point.x()-10, point.y()-10, point.x()+10, point.y()+10);

return (m_line.intersect(line, &intersectPnt)==QLineF::BoundedIntersection);
}

Thankyou for your help guys - problem solved

Yuriy Stepchenkov
2nd September 2015, 17:30

bool LineObject::pick_line(const QPointF &p) const
{
// get distance between point and current line
qreal d = dist(p, m_line);

// taking into account view scale factor,
// assume scale with constant aspect ratio (sx=sy)
// and we have only one view
const QGraphicsView *pview = graphics_view();
if (NULL != pview)
d *= (0.5*(pview->transform().m11() + pview->transform().m22()));

// line picked if point is close to it
return (d < std::max(s_selection_point_size, m_pen.widthF()));
}

qreal LineObject::dist(const QPointF &p, const QLineF &line) const
{
// transform to loocal coordinates system (0,0) - (lx, ly)
QPointF p1 = line.p1();
QPointF p2 = line.p2();
qreal x = p.x() - p1.x();
qreal y = p.y() - p1.y();
qreal x2 = p2.x() - p1.x();
qreal y2 = p2.y() - p1.y();

// if line is a point (nodes are the same) =>
// just return distance between point and one line node
qreal norm = sqrt(x2*x2 + y2*y2);
if (norm <= std::numeric_limits<int>::epsilon())
return sqrt(x*x + y*y);

// distance
return fabs(x*y2 - y*x2) / norm;
}