Algorithm for finding the segment overlapping two collinear segments

与世无争的帅哥 提交于 2019-12-03 14:12:27

This problem is roughly equivalent to the test whether two axis-aligned rectangles intersect: you can threat every segment as the diagonal of an axis-aligned rectangle, then you need to find the intersection of these two rectangles. The following is the approach I use for rectangle intersection.

Let's assume that the slope of the segments is ascending, vertical, or horizontal; if the segments are descending, negate every y-coordinate so that they are ascending.

Define the MinPoint and MaxPoint for each line segment:

Point min1 = new Point(Math.Min(line1.X1, line1.X2),Math.Min(line1.Y1,line1.Y2);
Point max1 = new Point(Math.Max(line1.X1, line1.X2),Math.Max(line1.Y1,line1.Y2);

Point min2 = new Point(Math.Min(line2.X1, line2.X2),Math.Min(line2.Y1,line2.Y2);
Point max2 = new Point(Math.Max(line2.X1, line2.X2),Math.Max(line2.Y1,line2.Y2);

Now the intersection is given by the following two points: the maximum of the two minimums, and the minimum of the two maximums

Point minIntersection = new Point(Math.Max(min1.X, min2.X), Math.Max(min1.Y, min2.Y));
Point maxIntersection = new Point(Math.Min(max1.X, max2.X), Math.Min(max1.Y, max2.Y));

and that's it. To test whether the two segments intersect at all, you check

bool intersect = (minIntersection.X< maxIntersection.X) && (minIntersection.Y< maxIntersection.Y);

If they do intersect, the intersection is given by the two points minIntersection and maxIntersection. If they do not intersect, the length of the segment (minIntersection, maxIntersection) is the distance between the two original segments.

(If you negated every y-coordinate in the first step, negate the y-coordinate of the result now)

(You can easily extend this approach to cover colinear segments in 3 or more dimensions)

Transform your coordinates so that the line though your segments is the new x-axis.

Sort the points in order from left to right.

If the segments do actually overlap, then the solution will always be the second and third points.

Note: If you aren't guaranteed that the segments overlap, then the test is simple - if the first two points belong to the same segment, then they do not overlap.

You lexicographically sort the two segments, then take the last element of the first segment and first element of the last segment (segments in lexicographical order). This gives you the segment you need.

You can then use the cross-product to determine if they form a line if you want to verify. The 2D cross-product being zero shows that three points form a line.

For example:

B = ((0,0),(3,3))
A = ((2,2),(5,5))

After a lexigraphic sort:

[((0,0),(3,3)),((2,2),(5,5))]
C = ((3,3),(4,4))

You can then check if they overlap by ensuring that the the first element of C is lexigraphically greater than the first element of the second line segment. In the example it is.

And the cross-products for certification of them overlapping. This is done by using the first element of the first list and the last element of the last segment. Then checking each of two elements inner individually to see if they are all in a line via the cross-product of the three points being zero:

cross((0,0),(1,1),(5,5)) = 0
cross((0,0),(4,4),(5,5)) = 0

Therefore the two input segments do form a line.

I'm not familiar enough with C# to ensure correctness, but in Python the algorithm looks like:

def cross(o, a, b):
    return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0])

def line_segment_overlap(segment_a,segment_b):
    segments = [segment_a.sort(),segment_b.sort()] # 2D Lexicographic sort the line segments
    segments.sort()
    A = segments[0]
    B = segments[1]
    if cross(A[0],A[1],B[1]) == 0 and cross(A[0],B[0],B[1]) == 0: # Segments form a line
        if A[1] > B[0]: # Segments overlap
            return (A[1],B[1]) # New line segment
    return None

Use the transform that maps the segment AB to (0, 0):(0, 1). Any segment that is collinear with AB will have ordinate 0 for both endpoints, let (c, 0):(d, 0). The overlap is given by (Max(0, c), 0):(Min(1, d), 0), unless Max(0, c) > Min(1, d).

Let ABx = X1 - X0, ABy = Y1 - Y0 and AB^2 = ABx^2 + ABy^2.

x = ((X-XA) ABx + (Y-YA) ABy) / AB^2
y = ((X-XA) ABy - (Y-YA) ABx) / AB^2

If you want the overlap in 2D, apply the inverse transform (with y = 0).

X = XA + x ABx
Y = YA + x ABy

I am not 100% sure of this, hopefully the community will tell. I am not posting this as a comment simply because I think that some minor formatting won't hurt.

The way that I see it, the equation of a straight line:

y = mx + c

where

  • y is a given y co-ordinate (in an x-y pair);
  • m is the gradient (slope) of the line;
  • x is a given co-ordinate (the other part of the x-y pair);
  • c is the intercept is key.

For any set of two points, you should be able to compute the equation for the line by:

  1. Finding the value of m (the usual dy/dx)

  2. Find the value of c for y=0.

Once that you will have computed that, you could generate the equation in the form of a string if you will.

Once that you will have gone through all the pairs of points, you should be able to identify line segments that lie on top of each other by checking the equations you have generated. You could then use the co-ordinates of the points on the line to extrapolate 2 rectangles and see which one fits inside the other. This should help you identify which line is within what segment.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!