Bug 313731 - Arbitrary polyline selection on non related click
Summary: Arbitrary polyline selection on non related click
Status: RESOLVED FIXED
Alias: None
Product: GEF
Classification: Tools
Component: GEF-Legacy Draw2d (show other bugs)
Version: unspecified   Edit
Hardware: PC Windows XP
: P3 normal (vote)
Target Milestone: 3.6.1 (Helios SR1)   Edit
Assignee: Alex Boyko CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2010-05-20 10:14 EDT by Vijay Raj CLA
Modified: 2010-09-07 15:14 EDT (History)
3 users (show)

See Also:


Attachments
patch (991 bytes, patch)
2010-05-20 22:26 EDT, Alex Boyko CLA
no flags Details | Diff
patch (1.20 KB, patch)
2010-09-01 11:56 EDT, Alex Boyko CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Vijay Raj CLA 2010-05-20 10:14:27 EDT
Build Identifier: I20091030-1201

There is a class in draw2d called Geometry which is being used in polyline.containsPoint(int x, int y) 

This has a method called segmentContainsPoint which uses Dot product of two vectors formula to calculate the distance between the point and the line segment...


 /**
     * @return true if the least distance between point (px,py) and segment (x1,y1) - (x2,y2) is
     *         less then specified tolerance
     */
    private static boolean segmentContainsPoint(int x1, int y1, int x2, int y2, int px, int py,
            int tolerance)
    {
        /*
         * Point should be located inside Rectangle(x1 -+ tolerance, y1 -+ tolerance, x2 +-
         * tolerance, y2 +- tolerance)
         */
        Rectangle lineBounds = Rectangle.SINGLETON;
        lineBounds.setSize(0, 0);
        lineBounds.setLocation(x1, y1);
        lineBounds.union(x2, y2);
        lineBounds.expand(tolerance, tolerance);
        if (!lineBounds.contains(px, py))
        {
            return false;
        }

        /*
         * If this is horizontal, vertical line or dot then the distance between specified point
         * and segment is not more then tolerance (due to the lineBounds check above)
         */
        if (x1 == x2 || y1 == y2)
        {
            return true;
        }

        /*
         * Calculating square distance from specified point to this segment using formula for Dot
         * product of two vectors.
         */
        int v1x = x2 - x1;
        int v1y = y2 - y1;
        int v2x = px - x1;
        int v2y = py - y1;
        int numerator = v2x * v1y - v1x * v2y;
        int denominator = v1x * v1x + v1y * v1y;
        int squareDistance = (int) ((long) numerator * numerator / denominator);
        return squareDistance <= tolerance * tolerance;
    }


Here at the end ,the dot product formula is wrong the denominater should be modded,since it can be negative
and the distance can come out to be negative, which will be always less tolerance^2...

In certain scanarious the segmentcontainspoint returns true hence the selection. 

Reproducible: Sometimes
Comment 1 Alex Boyko CLA 2010-05-20 10:25:53 EDT
Are you talking about:
int denominator = v1x * v1x + v1y * v1y
?

It's the sum of squares... How could this be negative?
Comment 2 Vijay Raj CLA 2010-05-20 11:01:29 EDT
(In reply to comment #1)
> Are you talking about:
> int denominator = v1x * v1x + v1y * v1y
> ?
> 
> It's the sum of squares... How could this be negative?

(In reply to comment #1)
> Are you talking about:
> int denominator = v1x * v1x + v1y * v1y
> ?
> 
> It's the sum of squares... How could this be negative?

int squareDistance = (int) ((long) numerator * numerator /
denominator);

If the value of v1x * v1x + v1y * v1y becomes more then integer max,
then after casting it will become negative :)
Comment 3 Alex Boyko CLA 2010-05-20 11:54:54 EDT
Indeed, that makes sense, we've faced int overflows before :-)

Wonder if we can make this a bit better... like having long denominator and long numerator and to the cast to long for their calculation. Looks like denominator calculation may result in overflo too. What do you think?
Comment 4 Alexander Nyßen CLA 2010-05-20 12:07:48 EDT
Would't it be sufficient to change numerator, denumerator and squareDistance to long and do the comparison based on longs?
Comment 5 Vijay Raj CLA 2010-05-20 12:47:30 EDT
(In reply to comment #4)
> Would't it be sufficient to change numerator, denumerator and squareDistance to
> long and do the comparison based on longs?

yep that seems perfect solution(until it overflows someday ;D )
Comment 6 Alex Boyko CLA 2010-05-20 22:26:49 EDT
Created attachment 169436 [details]
patch

Alexander, Vijay
can you code review / buddy test this patch please?
Comment 7 Vijay Raj CLA 2010-05-21 00:59:14 EDT
(In reply to comment #6)
> Created an attachment (id=169436) [details]
> patch
> 
> Alexander, Vijay
> can you code review / buddy test this patch please?

Hi Alex

this will not work.

int numerator = v2x * v1y - v1x * v2y;
int denominator = v1x * v1x + v1y * v1y;

long numerator = (long) v2x * v1y - v1x * v2y;
long denominator = (long) v1x * v1x + v1y * v1y;
long squareDistance = numerator * numerator / denominator;

I tryed multiplying two big integers so that the result is long.
it first overflows and then is is casted to long.

So 

long v1y = y2 - y1;
long v2x = px - x1;
long v2y = py - y1;

long numerator = v2x * v1y - v1x * v2y;
long denominator = v1x * v1x + v1y * v1y;

long numerator = v2x * v1y - v1x * v2y;
long denominator = v1x * v1x + v1y * v1y;
long squareDistance = numerator * numerator / denominator;

"It is inevitable, Mr Anderson"

everything should be long cuz,v1x * v1x can overflow.
Comment 8 Vijay Raj CLA 2010-05-21 01:05:03 EDT
This dot product may be used at many places, what about them.
For example In GMF,and many other calculations(i do not know of any apart from PolyLineConnectionex class)
Comment 9 Alexander Nyßen CLA 2010-05-21 02:31:17 EDT
(In reply to comment #7)
> (In reply to comment #6)
> > Created an attachment (id=169436) [details] [details]
> > patch
> > 
> > Alexander, Vijay
> > can you code review / buddy test this patch please?
> 
> Hi Alex
> 
> this will not work.
> 
> int numerator = v2x * v1y - v1x * v2y;
> int denominator = v1x * v1x + v1y * v1y;
> 
> long numerator = (long) v2x * v1y - v1x * v2y;
> long denominator = (long) v1x * v1x + v1y * v1y;
> long squareDistance = numerator * numerator / denominator;
> 
> I tryed multiplying two big integers so that the result is long.
> it first overflows and then is is casted to long.
> 
> So 
> 
> long v1y = y2 - y1;
> long v2x = px - x1;
> long v2y = py - y1;
> 
> long numerator = v2x * v1y - v1x * v2y;
> long denominator = v1x * v1x + v1y * v1y;
> 
> long numerator = v2x * v1y - v1x * v2y;
> long denominator = v1x * v1x + v1y * v1y;
> long squareDistance = numerator * numerator / denominator;
> 
> "It is inevitable, Mr Anderson"
> 
> everything should be long cuz,v1x * v1x can overflow.

Right, the multiplication already has to be performed based on long precision.
Comment 10 Alex Boyko CLA 2010-09-01 11:56:44 EDT
Created attachment 177978 [details]
patch

Here is the corrected patch. If this one looks ok - I'll deliver it for 3.6.1
Comment 11 Anthony Hunter CLA 2010-09-07 15:00:06 EDT
(In reply to comment #10)
> Created an attachment (id=177978) [details]
> patch
> 
> Here is the corrected patch. If this one looks ok - I'll deliver it for 3.6.1

Looks ok to me,
Comment 12 Alex Boyko CLA 2010-09-07 15:14:20 EDT
Thanks, Anthony.
Delivered to R3_6_maintenance and HEAD.