Small level editor, but without export, sorry.
Code: Select all
-- bezier-roads
-- License cc0, darkfrei 2023
Active = {
x=0, y=400,
angle = 0,
-- type = "straight"
xEnd = 0,
yEnd = 0,
line= {},
}
Track = {}
--------------------------------------------------------
--------------------- special functions ----------------
--------------------------------------------------------
function nearestPointToRay(mx, my, startX, startY, angle)
local dx = mx - startX
local dy = my - startY
local s = math.sin(angle)
local c = math.cos(angle)
local rotatedX = dx * c + dy * s
local rotatedY = dx * s - dy * c
local t = math.max(rotatedX, 0)
return startX + t * c, startY + t * s
end
function addStraightRoad (length)
local x1, y1 = Active.x, Active.y
local angle = Active.angle
local x2 = x1 + length*math.cos(angle)
local y2 = y1 + length*math.sin(angle)
local line = {x1, y1, x2, y2}
-- local line = Active.line
local road = {line=line}
table.insert (Track, road)
-- new position
Active.x, Active.y = x2, y2
-- State.updateLines ()
end
function addCurveRoad ()
local road = {line=Active.line}
table.insert (Track, road)
Active.x, Active.y = Active.xEnd, Active.yEnd
Active.angle = Active.nextAngle
end
function getIntersection(startX, startY, startAngle, endX, endY)
local tangentX, tangentY = math.cos(startAngle), math.sin(startAngle)
local midX, midY = (startX + endX) / 2, (startY + endY) / 2
local rayAngle = math.atan2(midY - startY, midX - startX)
local rayX, rayY = math.cos(rayAngle), math.sin(rayAngle)
local t = ((midX - startX) * tangentX + (midY - startY) * tangentY) / (tangentX * rayX + tangentY * rayY)
return startX + t * tangentX, startY + t * tangentY
end
--------------------------------------------------------
--------------------- state machine --------------------
--------------------------------------------------------
States = {
straight = {name = "straight"},
curve = {name = "curve"},
}
State = States.straight
--State = States.curve
function States.straight.updateLines ()
local mx, my = love.mouse.getPosition ()
local x, y = Active.x, Active.y
local angle = Active.angle
local xEnd = x + 1000*math.cos(angle)
local yEnd = y + 1000*math.sin(angle)
-- ray to far point:
Active.controlPoints = {x, y, xEnd, yEnd}
xEnd, yEnd = nearestPointToRay(mx, my, x, y, angle)
Active.xEnd, Active.yEnd = xEnd, yEnd
Active.line = {x, y, xEnd, yEnd}
end
function States.straight.mousemoved (mx, my, dx, dy)
States.straight.updateLines (mx, my)
end
function States.straight.mousepressed (mx, my)
if Active.xEnd then
local dx = Active.xEnd - Active.x
local dy = Active.yEnd - Active.y
local length = (dx*dx+dy*dy)^0.5
if length > 10 then
addStraightRoad (length)
State = States.curve
State.updateLines ()
end
end
end
function States.curve.updateLines ()
local x, y = Active.x, Active.y
local startAngle = Active.angle
-- local xEnd, yEnd = mx, my
local mx, my = love.mouse.getPosition()
local controlX, controlY = getIntersection(x, y, startAngle, mx, my)
local endAngle = math.atan2(my - controlY, mx - controlX)
local controlPoints = {x, y, controlX, controlY, mx, my}
local bezierCurve = love.math.newBezierCurve( controlPoints )
local line = bezierCurve:render()
Active.line = line
Active.controlPoints = controlPoints
Active.nextAngle = endAngle
Active.xMiddle, Active.yMiddle = controlX, controlY
Active.xEnd, Active.yEnd = mx, my
end
function States.curve.mousemoved (mx, my, dx, dy)
States.curve.updateLines ()
end
function States.curve.mousepressed (mx, my)
if Active.xEnd then
local dx = Active.xEnd - Active.x
local dy = Active.yEnd - Active.y
local length = (dx*dx+dy*dy)^0.5
if length > 10 then
addCurveRoad ()
State.updateLines ()
end
end
end
function love.load ()
addStraightRoad (10)
State.updateLines ()
end
function love.draw ()
love.graphics.setLineWidth (2)
love.graphics.setColor (0.5,0.5,0)
if Active.controlPoints then
love.graphics.line (Active.controlPoints)
end
love.graphics.setColor (1,1,1)
if #Active.line > 2 then
love.graphics.line (Active.line)
end
love.graphics.setLineWidth (3)
love.graphics.setColor (1,1,1)
for i, road in ipairs (Track) do
if road.line then
love.graphics.line (road.line)
love.graphics.circle ('line', road.line[1], road.line[2], 1)
end
end
love.graphics.circle ('line', Active.x, Active.y, 1)
if Active.xEnd then
love.graphics.circle ('line', Active.xEnd, Active.yEnd, 4)
end
love.graphics.setColor (1,1,1)
love.graphics.print ('State: '..State.name,0,0)
love.graphics.print ('Press SPACE to change state',0,14)
love.graphics.print ('Click mouse to add the point',0,28)
love.graphics.print ('Press Esc to exit',0,42)
end
function love.mousemoved (mx, my, dx, dy)
State.mousemoved (mx, my, dx, dy)
end
function love.mousepressed (mx, my)
State.mousepressed (mx, my)
end
function save ()
end
function love.keypressed (key, scancode, isrepeat)
if key == "escape" then
save ()
love.event.quit()
elseif key == "space" then
if State.name == "straight" then
State = States.curve
else
State = States.straight
end
State.updateLines ()
else
end
end