Skip to content

Instantly share code, notes, and snippets.

@akkartik
Last active April 24, 2024 05:05
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save akkartik/20a0c7e7589c6179666fc78bc04b28b4 to your computer and use it in GitHub Desktop.
Save akkartik/20a0c7e7589c6179666fc78bc04b28b4 to your computer and use it in GitHub Desktop.
Little UI for compass-and-straightedge constructions using https://love2d.org
-- To run:
-- Download LÖVE from https://love2d.org
-- Download this file to a directory and rename it to `main.lua`
-- Run `love .` from that directory. (Replace 'love' with whatever the name of your LÖVE download is.)
function love.load()
-- maximize window
love.window.setMode(0, 0)
local width, height, flags = love.window.getMode()
-- shrink slightly to account for window decoration
width = width-100
height = height-100
love.window.setMode(width, height)
love.window.setTitle('Love Geometry')
local status_height = 20
UI = {
x=5,y=5, w=width-10,h=100, -- menu area
status_height=status_height,
x2=5,y2=110, w2=width-10, h2=height-status_height-115, -- shapes area
-- top-left coords for widgets on screen
colorx=25,colory=25, -- toggle between black and red
pointx=100,pointy=25, -- point tool
linex=175,liney=25, -- line tool
radiusx=250,radiusy=25, -- set radius of compass tool
compassx=325,compassy=25, -- compass tool
}
mouse = {x=0,y=0}
shapes_committed = {}
current_mode = 'line' -- valid modes: dot, line, radius, center, arcbegin, arcend
current_red = 0 -- red component of current color (green and blue are always 0)
current_radius = 100
current_end_angle = nil
pending = {}
end
function love.mousepressed(x, y, button)
if button == 1 then
if x>UI.colorx and x<UI.colorx+50 and y>UI.colory and y<UI.colory+50 then current_red = 1-current_red end
if x>UI.pointx and x<UI.pointx+50 and y>UI.pointy and y<UI.pointy+50 then current_mode = 'point' end
if x>UI.linex and x<UI.linex+50 and y>UI.liney and y<UI.liney+50 then current_mode = 'line' end
if x>UI.radiusx and x<UI.radiusx+50 and y>UI.radiusy and y<UI.radiusy+50 then current_mode = 'radius' end
if x>UI.compassx and x<UI.compassx+50 and y>UI.compassy and y<UI.compassy+50 then current_mode = 'center' end
if x>UI.x2 and x<UI.x2+UI.w2 and y>UI.y2 and y<UI.y2+UI.h2 then
if current_mode == 'point' then
table.insert(shapes_committed, {shape='point', x=x, y=y, R=current_red})
elseif current_mode == 'line' then
pending = {shape = 'line', x1=x,y1=y, R=current_red}
elseif current_mode == 'radius' then
pending = {shape = 'radius', x1=x,y1=y}
elseif current_mode == 'center' then
table.insert(shapes_committed, {shape='point', x=x, y=y, R=current_red})
pending = {shape='arc', cx=x,cy=y, r=current_radius, R=current_red}
current_mode = 'arcbegin'
elseif current_mode == 'arcbegin' then
assert(pending.shape == 'arc')
pending.s = angle(pending.cx,pending.cy, x,y)
current_mode = 'arcend'
end
end
end
end
function love.mousereleased(x,y, button)
if x>UI.x2 and x<UI.x2+UI.w2 and y>UI.y2 and y<UI.y2+UI.h2 then
if pending.shape == 'line' then
pending.x2 = x
pending.y2 = y
table.insert(shapes_committed, pending)
pending = {}
elseif current_mode == 'radius' then
-- radius widget doesn't draw anything
current_radius = math.dist(pending.x1,pending.y1, x,y)
pending = {}
current_end_angle = nil
elseif current_mode == 'arcend' then
pending.e = current_end_angle
table.insert(shapes_committed, pending)
current_mode = 'center'
pending = {}
current_end_angle = nil
end
end
end
function love.update()
mouse.x = love.mouse.getX()
mouse.y = love.mouse.getY()
end
function love.draw()
draw_shapes()
draw_menu()
end
function draw_menu()
love.graphics.setColor(0.2, 0.2, 0.2)
love.graphics.rectangle('fill', UI.x,UI.y, UI.w,UI.h)
local width, height, flags = love.window.getMode()
love.graphics.setColor(1, 1, 1)
love.graphics.print(mouse.x, 5, height-UI.status_height)
love.graphics.print("|", 35,height-UI.status_height)
love.graphics.print(mouse.y, 45, height-UI.status_height)
love.graphics.setColor(current_red, 0, 0)
love.graphics.rectangle('fill', UI.colorx,UI.colory, 50,50)
if current_mode == 'point' then
love.graphics.setColor(1, 1, 1)
love.graphics.rectangle('fill', UI.pointx,UI.pointy, 50,50)
love.graphics.setColor(current_red, 0, 0)
love.graphics.circle('fill', UI.pointx+25,UI.pointy+25, 3)
else
love.graphics.setColor(0.75, 0.75, 0.75)
love.graphics.rectangle('fill', UI.pointx,UI.pointy, 50,50)
love.graphics.setColor(0, 0, 0)
love.graphics.circle('fill', UI.pointx+25,UI.pointy+25, 3)
end
if current_mode == 'line' then
love.graphics.setColor(1, 1, 1)
love.graphics.rectangle('fill', UI.linex,UI.liney, 50,50)
love.graphics.setColor(current_red, 0, 0)
love.graphics.line(UI.linex+5,UI.liney, UI.linex+45,UI.liney+50)
else
love.graphics.setColor(0.75, 0.75, 0.75)
love.graphics.rectangle('fill', UI.linex,UI.liney, 50,50)
love.graphics.setColor(0, 0, 0)
love.graphics.line(UI.linex+5,UI.liney, UI.linex+45,UI.liney+50)
end
if current_mode == 'radius' then
love.graphics.setColor(1, 1, 1)
love.graphics.rectangle('fill', UI.radiusx,UI.radiusy, 50,50)
love.graphics.setColor(current_red, 0, 0)
love.graphics.circle('fill', UI.radiusx+10,UI.radiusy+25, 3)
love.graphics.line(UI.radiusx+10,UI.radiusy+25, UI.radiusx+40,UI.radiusy+25)
love.graphics.circle('fill', UI.radiusx+40,UI.radiusy+25, 3)
love.graphics.setColor(0.5, 0.5, 0.5)
love.graphics.arc('line', 'open', UI.radiusx+10,UI.radiusy+25, 30, 1/2,-1/2)
else
love.graphics.setColor(0.75, 0.75, 0.75)
love.graphics.rectangle('fill', UI.radiusx,UI.radiusy, 50,50)
love.graphics.setColor(0, 0, 0)
love.graphics.circle('fill', UI.radiusx+10,UI.radiusy+25, 3)
love.graphics.line(UI.radiusx+10,UI.radiusy+25, UI.radiusx+40,UI.radiusy+25)
love.graphics.circle('fill', UI.radiusx+40,UI.radiusy+25, 3)
love.graphics.setColor(0.5, 0.5, 0.5)
love.graphics.arc('line', 'open', UI.radiusx+10,UI.radiusy+25, 30, 1/2,-1/2)
end
if current_mode == 'center' or current_mode == 'arcbegin' then
love.graphics.setColor(1, 1, 1)
love.graphics.rectangle('fill', UI.compassx, UI.compassy, 50,50)
love.graphics.setColor(0.5, 0.5, 0.5)
love.graphics.line(UI.compassx+10,UI.compassy+25, UI.compassx+40,UI.compassy+25)
love.graphics.setColor(current_red, 0, 0)
love.graphics.arc('line', 'open', UI.compassx+10,UI.compassy+25, 30, 1/2,-1/2)
else
love.graphics.setColor(0.75, 0.75, 0.75)
love.graphics.rectangle('fill', UI.compassx, UI.compassy, 50,50)
love.graphics.setColor(0.5, 0.5, 0.5)
love.graphics.line(UI.compassx+10,UI.compassy+25, UI.compassx+40,UI.compassy+25)
love.graphics.setColor(0, 0, 0)
love.graphics.arc('line', 'open', UI.compassx+10,UI.compassy+25, 30, 1/2,-1/2)
end
end
function draw_shapes()
love.graphics.setColor(1,1,1)
love.graphics.rectangle('fill', UI.x2,UI.y2, UI.w2,UI.h2)
for i, v in ipairs(shapes_committed) do
love.graphics.setColor(v.R, 0, 0)
if v.shape == 'line' then
love.graphics.line(v.x1,v.y1, v.x2,v.y2)
elseif v.shape == 'point' then
love.graphics.circle('fill', v.x,v.y, 3)
elseif v.shape == 'arc' then
love.graphics.arc('line', 'open', v.cx,v.cy, v.r, v.s,v.e, 360)
end
end
if pending.shape == 'arc' then
love.graphics.setColor(0.75, 0.75, 0.75)
love.graphics.circle('line', pending.cx,pending.cy, pending.r)
end
if love.mouse.isDown('1') then
if pending.shape == 'line' then
love.graphics.setColor(pending.R, 0, 0)
love.graphics.line(pending.x1,pending.y1, mouse.x,mouse.y)
elseif pending.shape == 'radius' then
love.graphics.setColor(0.75, 0.75, 0.75)
love.graphics.line(pending.x1,pending.y1, mouse.x,mouse.y)
elseif pending.shape == 'arc' then
if pending.s then
current_end_angle = angle_with_hint(pending.cx,pending.cy, mouse.x,mouse.y, current_end_angle)
love.graphics.setColor(pending.R, 0, 0)
love.graphics.arc('line', 'open', pending.cx,pending.cy, pending.r, pending.s, current_end_angle, 360)
end
end
end
end
function angle_with_hint(x1, y1, x2, y2, hint)
local result = angle(x1,y1, x2,y2)
if hint then
-- Smooth the discontinuity where angle goes from positive to negative.
-- The hint is a memory of which way we drew it last time.
while result > hint+math.pi/10 do
result = result-math.pi*2
end
while result < hint-math.pi/10 do
result = result+math.pi*2
end
end
return result
end
-- result is from -π/2 to 3π/2, approximately adding math.atan2 from Lua 5.3
-- (LÖVE is Lua 5.1)
function angle(x1,y1, x2,y2)
local result = math.atan((y2-y1)/(x2-x1))
if x2 < x1 then
result = result+math.pi
end
return result
end
function math.dist(x1,y1, x2,y2) return ((x2-x1)^2+(y2-y1)^2)^0.5 end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment