0

I would like to be able to detect click on a wide line. I've got a function taken from there which manages to detect if a point is exactly on it, but I'd like to add some tolerance to detect click on all the rendered line.

Here is my current function, (taken from there Determine if a point is between 2 other points on a line) :

    public bool PointIsOnLine(Vector2 currPoint, UILineRenderer line)
{
    Vector2 point1 = line.points[0];
    Vector2 point2 = line.points[1];

    float dxc = currPoint.x - point1.x;
    float dyc = currPoint.y - point1.y;

    float dxl = point2.x - point1.x;
    float dyl = point2.y - point1.y;

    float cross = dxc * dyl - dyc * dxl;

    if (cross != 0)
        return false;


    if (Mathf.Abs(dxl) >= Mathf.Abs(dyl))
        return dxl > 0 ?
          point1.x <= currPoint.x && currPoint.x <= point2.x :
          point2.x <= currPoint.x && currPoint.x <= point1.x;
    else
        return dyl > 0 ?
          point1.y <= currPoint.y && currPoint.y <= point2.y :
          point2.y <= currPoint.y && currPoint.y <= point1.y;
}

You can access the line's thickness using line.thickness

Majellan
  • 25
  • 5

2 Answers2

3

To check if a point is near a line segment you would first find the projection of the point on the line:

var dirNorm = (point2 - point1).Normalized;
var t = Vector2.Dot(currPoint - point1, dirNorm );

Next step is to clamp the t value. If you have a line segment the limits should be [0, Length].

var tClamped = Mathf.Clamp(t, 0, (point2 - point1).Length); 

You can then convert it back to a point by

var closestPoint = point1 + dirNorm  * tClamped;

And you can then get the distance by

var dist = Vector2.Distance(currPoint, closestPoint);

Add a check to see if the distance is smaller than some value and you get your answer.

Note that you would want to keep things as vectors as much as possible. Don't go down to individual x/y values unless you absolutely have to. Unity3D have a fairly good selection of vector functions, use them! Also note that this could probably be optimized a bit, but it may be useful to divide this in individual functions, i.e. one function get the t-value, one to get the closest point, another to get the distance etc. These functions can probably be reused for other things.

JonasH
  • 28,608
  • 2
  • 10
  • 23
  • Thanks for your reply, I'll try your solution! I have found a working solution though, tell me what you think about it – Majellan Jun 13 '22 at 11:07
  • @Majellan I'm really not a fan of any solution that splits vectors into individual components. And I'm not sure about the math, my understanding is that the 2D cross-product is the area of the parallelogram of the two vectors, and that would not give a correct solution. – JonasH Jun 13 '22 at 12:25
  • I'm trying your solution but I don't get what tClamped is? How should I get its value? – Majellan Jun 14 '22 at 13:19
  • @Majellan `tClamped` is the t variable [clamped to a range](https://en.wikipedia.org/wiki/Clamping_(graphics)) – JonasH Jun 14 '22 at 13:34
  • Ok I see. I have modified the code below with your solution. It is far more precise than what it used to be! Thank you so much! – Majellan Jun 14 '22 at 14:19
0

Here is my (working) solution now, thanks to the help of @JonasH!

public bool PointIsOnLine(Vector3 point, UILineRenderer line)
    {
        Vector3 point1 = line.points[0];
        Vector3 point2 = line.points[1];

        var dirNorm = (point2 - point1).normalized;
        var t = Vector2.Dot(point - point1, dirNorm);
        var tClamped = Mathf.Clamp(t, 0, (point2 - point1).magnitude);
        var closestPoint = point1 + dirNorm * tClamped;

        var dist = Vector2.Distance(point, closestPoint);


        if(dist < line.thickness / 2)
        {
            return true;
        }

        return false;
    }
Majellan
  • 25
  • 5