Skip to content

Instantly share code, notes, and snippets.

@Anaminus
Last active September 29, 2020 01:40
Show Gist options
  • Save Anaminus/9469879 to your computer and use it in GitHub Desktop.
Save Anaminus/9469879 to your computer and use it in GitHub Desktop.
DrawArcs
-- LOVE2D 0.9.0
-- main.lua
-- Left-click somewhere to add point
-- Left-click and drag green point to move it
-- Right-click green point to remove it
-- Left-click and drag red point to modify starting angle
-- Backspace to remove all points
local Vector2;Vector2 = {
new = function(x,y)
return setmetatable({x=tonumber(x) or 0,y=tonumber(y) or 0},Vector2.mt)
end;
mt = {
__index = {
magnitude = function(v) return math.sqrt(v.x^2 + v.y^2) end;
unit = function(v) return Vector2.new(v.x/v:magnitude(),v.y/v:magnitude()) end;
dot = function(a,b) return a.x*b.x + a.y*b.y end;
perp = function(v) return Vector2.new(v.y,-v.x) end;
};
__eq = function(a,b)
return a.x == b.x and a.y == b.y
end;
__add = function(a,b)
return Vector2.new(a.x + b.x, a.y + b.y)
end;
__sub = function(a,b)
return Vector2.new(a.x - b.x, a.y - b.y)
end;
__mul = function(a,b)
if type(a) == 'number' then
return Vector2.new(a*b.x, a*b.y)
elseif type(b) == 'number' then
return Vector2.new(a.x*b, a.y*b)
else
return Vector2.new(a.x*b.x, a.y*b.y)
end
end;
__unm = function(v)
return Vector2.new(-v.x,-v.y);
end;
__div = function(a,b)
if type(a) == 'number' then
return Vector2.new(a/b.x, a/b.y)
elseif type(b) == 'number' then
return Vector2.new(a.x/b, a.y/b)
else
return Vector2.new(a.x/b.x, a.y/b.y)
end
end;
__tostring = function(object)
return '[' .. object.x .. ', ' .. object.y .. ']'
end;
};
}
-----------------------
local gfx = love.graphics
local function normal(n)
if n == 0 then return 0 end
return n/math.abs(n)
end
local function drawArc(c,r,a,x,y,s)
local p = Vector2.new(-1,0)
local q = (x-c):unit()
local aq = q:perp():dot(p)
local o = math.acos(p:dot(q))*-normal(aq)
if a > 0 then o = o + math.pi end
for i = 1,s do
local a0 = (i-1)/s*a + o
local a1 = i/s*a + o
gfx.line(
c.x + (math.cos(a0) * r), c.y - (math.sin(a0) * r),
c.x + (math.cos(a1) * r), c.y - (math.sin(a1) * r)
)
end
end
local pressed = false
local p = 1
local points = {}
local tanAngle = -math.pi/4
local tanPos = Vector2.new()
function love.update()
if pressed then
if p == 0 and points[1] and points[2] then
local t = Vector2.new(love.mouse.getPosition())
local x = points[1]
local y = points[2]
local yx = y-x
local yxu = yx:unit()
local txu = (t-x):unit()
local length = yx:magnitude()
local aq = txu:perp():dot(yxu)
local a = math.acos(txu:dot(yxu))*normal(aq)
tanPos = Vector2.new(
yxu.x*math.cos(a) - yxu.y*math.sin(a),
yxu.x*math.sin(a) + yxu.y*math.cos(a)
)*64 + x
tanAngle = a
elseif p > 0 and p <= #points then
points[p].x,points[p].y = love.mouse.getPosition()
end
end
end
function love.draw()
gfx.reset()
gfx.setLineWidth(6)
gfx.setPointSize(12)
gfx.setBackgroundColor(102,102,102)
for i = 2,#points do
local a = points[i-1]
local b = points[i]
gfx.setColor(0,128,0)
gfx.line(a.x,a.y,b.x,b.y)
end
if #points > 1 then
local a = tanAngle
for i = 1,#points-1 do
local X = points[i]
local Y = points[i+1]
local Z = points[i+2]
local r = (Y-X):magnitude()/2/math.sin(a)
local YXu = (Y-X):unit()
local a90 = a - math.pi/2
local C = Vector2.new(
YXu.x*math.cos(a90) - YXu.y*math.sin(a90),
YXu.x*math.sin(a90) + YXu.y*math.cos(a90)
)*r + X
-- draw circle
-- gfx.setColor(128,128,128,128)
-- gfx.circle('fill',C.x,C.y,r,100)
-- gfx.setColor(77,77,77,128)
-- gfx.circle('line',C.x,C.y,r,100)
-- draw arc
gfx.setColor(255,0,0)
drawArc(C,r,a*2,X,Y,100)
-- draw radius
-- gfx.setColor(0,0,128)
-- gfx.line(X.x,X.y,C.x,C.y)
-- draw center
-- gfx.setColor(0,255,255)
-- gfx.point(C.x,C.y)
if Z then
local YCu = (Y-C):unit()*normal(a)
local YCup = YCu:perp()
local ZYu = (Z-Y):unit()
local aq = YCu:dot(ZYu)
a = math.acos(YCup:dot(ZYu))*-normal(aq)
-- draw tangent
-- local p = YCup*64 + Y
-- local q = -YCup*64 + Y
-- gfx.setColor(128,0,0)
-- gfx.line(p.x,p.y,q.x,q.y)
end
end
end
if #points > 1 then
local x = points[1]
local y = points[2]
local yxu = (y-x):unit()
tanPos = Vector2.new(
yxu.x*math.cos(tanAngle) - yxu.y*math.sin(tanAngle),
yxu.x*math.sin(tanAngle) + yxu.y*math.cos(tanAngle)
)*64 + x
gfx.setLineWidth(6)
gfx.setColor(128,0,0)
gfx.line(tanPos.x,tanPos.y,x.x,x.y)
gfx.setLineWidth(1)
gfx.setColor(128,0,0)
gfx.circle('fill',tanPos.x,tanPos.y,6)
gfx.setColor(255,255,255)
gfx.circle('line',tanPos.x,tanPos.y,6)
end
gfx.setLineWidth(1)
for i = 1,#points do
local v = points[i]
gfx.setColor(0,255,0)
gfx.circle('fill',v.x,v.y,6)
gfx.setColor(255,255,255)
gfx.circle('line',v.x,v.y,6)
end
end
function love.mousepressed(x,y,b)
if b ~= 'l' and b ~= 'r' then return end
local c = Vector2.new(x,y)
if #points > 1 and b == 'l' then
if (c-tanPos):magnitude() < 8 then
p = 0
pressed = true
return
end
end
local n
for i = 1,#points do
if (c-points[i]):magnitude() < 8 then
n = i
break
end
end
if b == 'l' then
if n then
p = n
else
points[#points+1] = c
p = #points
end
pressed = true
elseif b == 'r' then
if n and not pressed then
table.remove(points,n)
end
end
end
function love.mousereleased(x,y,b)
if b == 'l' then
pressed = false
end
end
function love.keypressed(k)
if k == 'backspace' and not pressed then
points = {}
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment