21
2
This challenge is based on actual collision detection I had to write for a simple game recently.
Write a program or function which, given two objects, returns a truthy or falsy value depending on whether the two objects are in collision (i.e. intersect) or not.
You need to support three types of objects:
- Line segments: represented by 4 floats, indicating the two endpoints, i.e. (x1,y1) and (x2,y2). You may assume that the endpoints are not identical (so the line segment is not degenerate).
- Discs: i.e. filled circles, represented by 3 floats, two for the centre (x,y) and one (positive) for the radius r.
- Cavities: these are a disc's complement. That is, a cavity fills all of 2D space, except a circular region, specified by a centre and radius.
Your program or function will receive two such objects in the form of an identifying integer (of your choice) and their 3 or 4 floats. You can take input via STDIN, ARGV or function argument. You may represent the input in any convenient form that is not preprocessed, e.g. 8 to 10 individual numbers, two comma-separated list of values or two lists. The result can be returned or written to STDOUT.
You may assume that the objects are either at least 10-10 length units apart or intersect by that much, so you don't need to worry about the limitations of floating point types.
This is code golf, so the shortest answer (in bytes) wins.
Test Cases
Representing line segments with 0
, discs with 1
and cavities with 2
, using a list-based input format, the following should all produce a truthy output:
[0,[0,0],[2,2]], [0,[1,0],[2,4]] # Crossing line segments
[0,[0.5,0],[-0.5,0]], [1,[0,0],1] # Line contained in a disc
[0,[0.5,0],[1.5,0]], [1,[0,0],1] # Line partially within disc
[0,[-1.5,0.5],[1.5,0.5]], [1,[0,0],1] # Line cutting through disc
[0,[0.5,2],[-0.5,2]], [2,[0,0],1] # Line outside cavity
[0,[0.5,0],[1.5,0]], [2,[0,0],1] # Line partially outside cavity
[0,[-1.5,0.5],[1.5,0.5]], [2,[0,0],1] # Line cutting through cavity
[1,[0,0],1], [1,[0,0],2] # Disc contained within another
[1,[0,0],1.1], [1,[2,0],1.1] # Intersecting discs
[1,[3,0],1], [2,[0,0],1] # Disc outside cavity
[1,[1,0],0.1], [2,[0,0],1] # Disc partially outside cavity
[1,[0,0],2], [2,[0,0],1] # Disc encircling cavity
[2,[0,0],1], [2,[0,0],1] # Any two cavities intersect
[2,[-1,0],1], [2,[1,0],1] # Any two cavities intersect
while the following should all result in a falsy output
[0,[0,0],[1,0]], [0,[0,1],[1,1]] # Parallel lines
[0,[-2,0],[-1,0]], [0,[1,0],[2,0]] # Collinear non-overlapping lines
[0,[0,0],[2,0]], [0,[1,1],[1,2]] # Intersection outside one segment
[0,[0,0],[1,0]], [0,[2,1],[2,3]] # Intersection outside both segments
[0,[-1,2],[1,2]], [1,[0,0],1] # Line passes outside disc
[0,[2,0],[3,0]], [1,[0,0],1] # Circle lies outside segment
[0,[-0.5,0.5],[0.5,-0.5]], [2,[0,0],1] # Line inside cavity
[1,[-1,0],1], [1,[1,1],0.5] # Non-intersecting circles
[1,[0.5,0],0.1], [2,[0,0],1] # Circle contained within cavity
Trickier than I thought at first. Starting with the line/line case, I come upon a surprising number of edge cases. Couldn't you disallow collinear segments? Would make things much easier. ;) – Emil – 2014-10-08T21:06:59.593
@Emil Sorry, but 9 hours after posting, I'll have to assume that others might have started working on the challenge, and changing the spec (apart from fixing breaking problems) doesn't seem like a good idea to me. Depending on how you do it, parallel line segments should be the only edge case you need to worry about for the line-line collisions, though, I think. – Martin Ender – 2014-10-08T21:12:22.457
Sure, I wasn't really expecting you change it. I was just a tiny bit frustrated that handling the different variants of collinear line segments doubled the length of my code so far. :) (A great challenge, by the way!) – Emil – 2014-10-08T21:30:54.293
Doesn't collinear points fall under "doesn't collide by 10^-10"? – TwiNight – 2014-10-10T01:01:41.780
@TwiNight Not if the two lines are collinear but don't overlap. E.g.
[0,[-2,0],[-1,0]], [0,[1,0],[2,0]]
– Martin Ender – 2014-10-10T08:36:37.180In short, we can assume parallel lines (collinear or not) to be non-colliding? – TwiNight – 2014-10-10T09:14:54.550
@TwiNight Yes, that's fine. – Martin Ender – 2014-10-10T09:15:55.790
I asked just out of curiosity, as my approach was not solving linear equations, but I later found out that not only do I have to use linear equations but I also have to correctly handle collinear points...... – TwiNight – 2014-10-11T03:52:01.087
Can we use polymorphism to represent the shapes? As in,
data Shape = Line Pt Pt | Circle Pt double | Cavity Pt double; collide :: Shape -> Shape -> Bool
? – John Dvorak – 2014-10-11T11:09:16.707@JanDvorak I have no clue what syntax this is, but yes, you may use polymorphism. – Martin Ender – 2014-10-11T11:10:48.450
@MartinBüttner that's a Haskell type declaration. A Shape is either a Line defined by two points, a Circle defined by a point and a number, or a Cavity defined by a point and a number. The collide function takes two Shapes and returns a Boolean. – John Dvorak – 2014-10-11T11:13:35.763
I assume two cavities always intersect? – John Dvorak – 2014-10-11T11:16:32.347
@JanDvorak Yes, see the last two truthy examples ;) – Martin Ender – 2014-10-11T11:17:11.083
A plane-with-cavity intersection might be interesting in 4D. Intersect the planes => if they intersect at a point (the general case), check if the point lies within the cavity of at least one plane. – John Dvorak – 2014-10-11T11:19:48.840