2D Line reflection on a "mirror"

2.7k views Asked by At

So I've been working at this on and off for a week, googling and all and I haven't found how to do this.

I have a table of "rays" and a table of "lines", and I want the lines to act as mirrors and reflect a ray whenever the ray hits a line. Imagine a laser bouncing off mirrors, that sort of reflection. I've got the intersection detection working, but I can't figure out how to properly calculate the angle of reflection and extend the ray that direction.

Code:

--the table rays is a table of tables, and each table inside is formatted as such:
--rays[x] = {100,200,150,600,200,400}, where (100,200) are ordered pairs, etc.
--The table lines simply contains values for x1,y1,x2,y2

for i,ray in ipairs(rays) do
        for j,line in ipairs(lines) do
            if line.x2 ~= nil and #ray>3 then 
                print(line.x2..' '..line.y2)
                iX, iY = intersect.test(ray[#ray-3],ray[#ray-2],
                         ray[#ray-1],ray[#ray],line.x1,line.y1,line.x2,line.y2)

--The above code takes each ray and 
--sees if it intersects with a line, with the intersect.test function
--Then if it does, where iX and iY aren't nil, it continues

                if iX ~= nil and iY ~= nil then
                    local rayA = (180/math.pi)*math.atan(getSlope(ray[#ray-3],ray[#ray-2],ray[#ray-1],ray[#ray]))
                    local lineA = (180/math.pi)*math.atan(getSlope(line.x1,line.y1,line.x2,line.y2))
                    local normalA = (180/math.pi)*math.atan(-1/getSlope(line.x1,line.y1,line.x2,line.y2))

--Here I'm calculating the angle in degrees. For the final code all those atans will
--be out of there for optimization, but its easiest now to see the actual angle 

                    print(rayA..' '..lineA..' '..normalA)

                    ray[#ray-1]=iX
                    ray[#ray]=iY

--This little part just create a point on the ray right at the intersection
--The code after this is my attempt, which doesn't work

                    local reflectA = normalA-rayA
                    local reflectR = 2*reflectA+rayA

                    print(reflectR)

                    reflectR = reflectR/(180/math.pi)
                    local rSlope = math.tan(reflectR)

                    local offset = 0


                    ray[#ray+1]=iX+offset
                    ray[#ray+1]=iY+(offset*rSlope)
                end
            end
        end
    end

I'm stuck on that last section. It sort of creates a segment bouncing off the line, but sometimes it crosses over the line, and it never is the correct reflection angle. Any pointers on how I should do this would be greatly appreciated.

1

There are 1 answers

0
samgak On BEST ANSWER

It's better to avoid working with slopes and angles if you can avoid them, because you will have to deal with annoying special cases like when the slope is +ve or -ve infinity and so on.

If you can calculate the normal of the line (blue arrow), then you can use a dot product to do the reflection:

enter image description here

Calculating the normal for the line is done like this:

local normalY = line.x2 - line.x1
local normalX = line.y1 - line.y2
local normalLength = math.sqrt(normalX * normalX + normalY * normalY)
normalX = normalX / normalLength
normalY = normalY / normalLength

Then you need to calculate the vector that goes from the intersection point of the line and the ray to the tip of the ray (the point that has gone "through" the line that you want to reflect):

local rayX = rayTipX - iX
local rayY = rayTipY - iY

Then calculate the dot product:

local dotProduct = (rayX * normalX) + (rayY * normalY)

This tells us how far in the direction of the line normal the ray has gone past the intersection point (the length of the green line). To find the vector for the green line, multiply the line normal by the dot product:

 local dotNormalX = dotProduct * normalX
 local dotNormalY = dotProduct * normalY

If we negate this vector and then double it (to get the green line plus the pink line), and add it to the tip of the ray, we will get the reflected tip of the ray:

 local reflectedRayTipX = rayTipX - (dotNormalX * 2)
 local reflectedRayTipY = rayTipY - (dotNormalY * 2)