Skip to content

Instantly share code, notes, and snippets.

@jonbro
Created October 1, 2012 19:40
Show Gist options
  • Select an option

  • Save jonbro/3813982 to your computer and use it in GitHub Desktop.

Select an option

Save jonbro/3813982 to your computer and use it in GitHub Desktop.
Vec2 = class(function(vec,x,y)
vec.x = x
vec.y = y
end)
function Vec2:set(v)
self.x = v.x
self.y = v.y
return self
end
function Vec2:add(v)
self.x = self.x + v.x;
self.y = self.y + v.y;
return self
end
function Vec2:add(v)
self.x = self.x + v.x;
self.y = self.y + v.y;
return self
end
function Vec2.det(x1,y1, x2,y2)
return x1*y2 - y1*x2
end
function Vec2:mult(s) -- by scalar
self.x = self.x * s;
self.y = self.y * s;
return self
end
function Vec2:sub(v)
self.x = self.x - v.x;
self.y = self.y - v.y;
return self
end
function Vec2:length()
return math.sqrt(self.x*self.x + self.y * self.y)
end
function Vec2:normalize()
length = math.sqrt(self.x*self.x + self.y*self.y)
if length > 0 then
self.x = self.x/length
self.y = self.y/length
else
self.x = 0
self.y = 0
end
return self
end
function Vec2:rotate(a)
local px = self.x*math.cos(a) - self.y*math.sin(a)
local py = self.x*math.sin(a) + self.y*math.cos(a)
self.x, self.y = px, py
return self
end
function Vec2:copy()
return Vec2(self.x, self.y)
end
function Vec2:angle()
return math.atan2(self.x, self.y)
end
function Vec2:distance(v)
if not v then
print(debug.traceback())
end
return math.sqrt((self.x-v.x)^2 + (self.y-v.y)^2)
end
function Vec2:distanceSq(v)
if not v then
print(debug.traceback())
end
return (self.x-v.x)^2 + (self.y-v.y)^2
end
function Vec2:distanceSq(v)
if not v then
print(debug.traceback())
end
return (self.x-v.x)^2 + (self.y-v.y)^2
end
function Vec2:__eq(b)
return self.x == b.x and self.y == b.y
end
function Vec2:__tostring()
return "Vector: " .. self.x .. ", " .. self.y
end
Line2d = class(function(line,x1,y1,x2,y2)
line.v1 = Vec2(x1,y1)
line.v2 = Vec2(x2,y2)
end)
-- http://www.angelfire.com/fl/houseofbartlett/solutions/lineinter2d.html
function Line2d:check_tri_clock_dir(v1,v2,v3)
test = (((v2.x - v1.x)*(v3.y - v1.y)) - ((v3.x - v1.x)*(v2.y - v1.y)));
if (test > 0) then return 0 -- counter clockwise
elseif(test < 0) then return 1 -- clockwise
else return 3 end -- line
end
function Line2d:check_intersection(l2)
test1_a = self:check_tri_clock_dir(self.v1,self.v2,l2.v1)
test1_b = self:check_tri_clock_dir(self.v1,self.v2,l2.v2)
if(test1_a ~= test1_b) then
test2_a = self:check_tri_clock_dir(l2.v1,l2.v2,self.v1)
test2_b = self:check_tri_clock_dir(l2.v1,l2.v2,self.v2)
if(test2_a ~= test2_b) then
return true
end
end
return false
end
-- Rectangle Class
Rectangle = class(function(rect, x, y, w, h)
rect.w = w
rect.h = h
rect.pos = Vec2(0,0)
rect.x1 = 0
rect.y1 = 0
rect.x2 = 0
rect.y2 = 0
rect.red = 0;
rect.green = 0;
rect.blue = 0;
rect.tint = {r=255, g=255, b=255, a=255}
Rectangle.setPosition(rect, x, y)
rect.layer = 0
end)
function Rectangle:setPosition(x,y)
-- added some type coersion
self.pos.x = x+0
self.pos.y = y+0
self.x1 = x+0
self.y1 = y+0
self.x2 = x+self.w
self.y2 = y+self.h
end
function Rectangle:setColor(r,g,b)
self.red = r;
self.green = g;
self.blue = b;
Object.setColor(self, r,g,b)
end
function Rectangle:sync()
end
function Rectangle:draw()
-- draw the box
local c = {self.pos.x, self.pos.y, self.w, self.h}
local tl = {c[1], c[2]}
local tr = {c[1]+c[3], c[2]}
local bl = {c[1], c[2]+c[4]}
local br = {c[1]+c[3], c[2]+c[4]}
sheets["ui"]:addCornerTile(sprites["ui"]["white.png"], tl[1],tl[2], tr[1],tr[2], br[1],br[2], bl[1],bl[2], self.layer, self.tint.r, self.tint.g, self.tint.b, self.tint.a)
end
function Rectangle:drawDrop(s, color)
-- draw the dropshadow
local c = {self.pos.x, self.pos.y, self.w, self.h}
local tl = {c[1], c[2]}
local tr = {c[1]+c[3], c[2]}
local bl = {c[1], c[2]+c[4]}
local br = {c[1]+c[3], c[2]+c[4]}
local h = s
if not retina then h=h/2 end
tl = {bl[1], bl[2]}
tr = {br[1], br[2]}
bl = {bl[1]+h/2, bl[2]+h}
br = {br[1]-h/2, br[2]+h}
sheets["ui"]:addCornerTile(sprites["ui"]["white.png"], tl[1],tl[2], tr[1],tr[2], br[1],br[2], bl[1],bl[2], self.layer, color.r,color.g,color.b,255)
self:draw()
end
function Rectangle:drawTopDrop(h)
local c = {self.pos.x, self.pos.y, self.w, self.h}
local tl = {c[1], c[2]}
local tr = {c[1]+c[3], c[2]}
local bl = {c[1], c[2]+c[4]}
local br = {c[1]+c[3], c[2]+c[4]}
if not retina then h=h/2 end
tl = {tl[1], tl[2]}
tr = {tr[1], tr[2]}
bl = {tl[1], tl[2]-h}
br = {tr[1], tr[2]-h}
sheet:addCornerTile(sprites["white.png"], tl[1],tl[2], tr[1],tr[2], br[1],br[2], bl[1],bl[2], self.layer, 159,155,140,255)
self:draw()
end
function Rectangle:drawOutline()
-- tl to tr
lb:addLine(self.pos.x, self.pos.y, self.pos.x+self.w, self.pos.y)
-- tr to br
lb:addLine(self.pos.x+self.w, self.pos.y, self.pos.x+self.w, self.pos.y+self.h)
-- br to bl
lb:addLine(self.pos.x+self.w, self.pos.y+self.h, self.pos.x, self.pos.y+self.h)
-- bl to tl
lb:addLine(self.pos.x, self.pos.y+self.h, self.pos.x, self.pos.y)
end
function Rectangle:doesPointTouch(v)
if (v.x >= self.pos.x and v.x < self.pos.x+self.w) then
if (v.y >= self.pos.y and v.y < self.pos.y+self.h) then
return true
end
end
return false
end
function Rectangle:doesRectangleTouch(r)
return
(r.pos.x + r.w > self.pos.x) and
(r.pos.x < self.pos.x+self.w) and
(r.pos.y + r.h > self.pos.y) and
(r.pos.y < self.pos.y + self.h)
end
function Rectangle:whatSideDoesLineTouch(line, velocity)
-- check the top and bottom intersections
if velocity.y > 0 then
side = Line2d(self.x1,self.y1,self.x2,self.y1)
if(line:check_intersection(side)) then
return 1
end
elseif velocity.y < 0 then
side = Line2d(self.x1,self.y2,self.x2,self.y2)
if(line:check_intersection(side)) then
return 3
end
end
-- check the left and right intersections
if velocity.x > 0 then
side = Line2d(self.x1,self.y1,self.x1,self.y2)
if(line:check_intersection(side)) then
return 4
end
elseif velocity.x < 0 then
side = Line2d(self.x2,self.y1,self.x2,self.y2)
if(line:check_intersection(side)) then
return 2
end
end
return 0
end
-- returns if there is an intersection and the distance to the intersection
function Rectangle:raycast(o, d)
local ox, oy = o.x, o.y
local dx, dy = d.x, d.y
local tx_min, ty_min, tx_max, ty_max
local a = 1/dx
local mMin = self.pos
local mMax = Vec2(self.pos.x+self.w, self.pos.y+self.h)
-- x intersection
if a >= 0 then
tx_min = (mMin.x-ox)*a;
tx_max = (mMax.x-ox)*a;
else
tx_min = (mMax.x-ox)*a;
tx_max = (mMin.x-ox)*a;
end
local b = 1/dy
if b >= 0 then
ty_min = (mMin.y-oy)*b;
ty_max = (mMax.y-oy)*b;
else
ty_min = (mMax.y-oy)*b;
ty_max = (mMin.y-oy)*b;
end
local t0 = math.max(tx_min, ty_min)
local t1 = math.min(tx_max, ty_max)
if t0 < t1 and t1 > 0 then
return true, t0
end
return false
end
local a, b = {max={}, min={}}, {max={}, min={}}
local va, vb = Vec2(0,0), Vec2(0,0)
local u_0 = Vec2(0,0)
local u_1, displacement = Vec2(1, 1), Vec2(0,0)
-- tests if two moving rectangles intersect with each other, and if so at what point
function Rectangle:sweepTest(r2, r1Movement, r2Movement)
a.max.x, a.min.x, a.max.y, a.min.y = self.pos.x+self.w, self.pos.x, self.pos.y+self.h, self.pos.y
b.max.x, b.min.x, b.max.y, b.min.y = r2.pos.x+r2.w, r2.pos.x, r2.pos.y+r2.h, r2.pos.y
va.x, vb.x = r1Movement.x, r1Movement.y
vb.x, vb.y = r2Movement.x, r2Movement.y
-- get the displacement from the perspective of the first box
displacement.x, displacement.y = vb.x-va.x, vb.y-va.y
u_0.x, u_0.y = 0,0
u_1.x, u_1.y = 1, 1
-- if the objects were overlapping at the start of the frame
if (Object.overlap(self, r2)) then
return true, 0, 0
end
-- check on each axis
for i,v in ipairs({"x", "y"}) do
if a.max[v]<b.min[v] and displacement[v]<0 then
u_0[v] = (a.max[v] - b.min[v]) / displacement[v];
elseif b.max[v]<a.min[v] and displacement[v]>0 then
u_0[v] = (a.min[v] - b.max[v]) / displacement[v];
end
if b.max[v]>a.min[v] and displacement[v]<0 then
u_1[v] = (a.min[v] - b.max[v]) / displacement[v];
elseif a.max[v]>b.min[v] and displacement[v]>0 then
u_1[v] = (a.max[v] - b.min[v]) / displacement[v];
end
end
local u0 = math.max(u_0.x, u_0.y)
local u1 = math.min(u_1.x, u_1.y)
return u0<=u1, u0, u1
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment