add a level of indirection to vertices of shapes
This is like Sketchpad.
This commit is contained in:
parent
ece73b6683
commit
7d5a70379f
105
main.lua
105
main.lua
|
@ -75,7 +75,7 @@ function love.draw()
|
||||||
love.graphics.setColor(0, 0, 0)
|
love.graphics.setColor(0, 0, 0)
|
||||||
end,
|
end,
|
||||||
onpress1 = function()
|
onpress1 = function()
|
||||||
table.insert(lines, i, {y=y, h=256/2, shapes={}, pending={}})
|
table.insert(lines, i, {y=y, h=256/2, points={}, shapes={}, pending={}})
|
||||||
end})
|
end})
|
||||||
elseif type(line) == 'table' then
|
elseif type(line) == 'table' then
|
||||||
-- line drawing
|
-- line drawing
|
||||||
|
@ -87,14 +87,22 @@ function love.draw()
|
||||||
local mx,my = coord(love.mouse.getX()-16), coord(love.mouse.getY()-line.y)
|
local mx,my = coord(love.mouse.getX()-16), coord(love.mouse.getY()-line.y)
|
||||||
|
|
||||||
for _,shape in ipairs(line.shapes) do
|
for _,shape in ipairs(line.shapes) do
|
||||||
if on_shape(mx,my, shape) then
|
if on_shape(mx,my, line, shape) then
|
||||||
love.graphics.setColor(1,0,0)
|
love.graphics.setColor(1,0,0)
|
||||||
else
|
else
|
||||||
love.graphics.setColor(0,0,0)
|
love.graphics.setColor(0,0,0)
|
||||||
end
|
end
|
||||||
draw_shape(16,line.y, shape)
|
draw_shape(16,line.y, line, shape)
|
||||||
end
|
end
|
||||||
draw_pending_shape(16,line.y, line.pending)
|
for _,p in ipairs(line.points) do
|
||||||
|
if p.x == mx and p.y == my then
|
||||||
|
love.graphics.setColor(1,0,0)
|
||||||
|
else
|
||||||
|
love.graphics.setColor(0,0,0)
|
||||||
|
end
|
||||||
|
love.graphics.circle('line', pixels(p.x)+16,pixels(p.y)+line.y, 4)
|
||||||
|
end
|
||||||
|
draw_pending_shape(16,line.y, line)
|
||||||
else
|
else
|
||||||
love.graphics.draw(text, 25,y, 0, 1.5)
|
love.graphics.draw(text, 25,y, 0, 1.5)
|
||||||
end
|
end
|
||||||
|
@ -135,8 +143,8 @@ function love.mousereleased(x,y, button)
|
||||||
if lines.current.pending.mode == 'freehand' then
|
if lines.current.pending.mode == 'freehand' then
|
||||||
-- the last point added during update is good enough
|
-- the last point added during update is good enough
|
||||||
elseif lines.current.pending.mode == 'line' then
|
elseif lines.current.pending.mode == 'line' then
|
||||||
lines.current.pending.x2 = coord(x-16)
|
local j = insert_point(lines.current.points, coord(x-16), coord(y-lines.current.y))
|
||||||
lines.current.pending.y2 = coord(y-lines.current.y)
|
lines.current.pending.p2 = j
|
||||||
end
|
end
|
||||||
table.insert(lines.current.shapes, lines.current.pending)
|
table.insert(lines.current.shapes, lines.current.pending)
|
||||||
lines.current.pending = {}
|
lines.current.pending = {}
|
||||||
|
@ -153,7 +161,8 @@ function propagate_to_drawings(x,y, button)
|
||||||
if current_mode == 'freehand' then
|
if current_mode == 'freehand' then
|
||||||
drawing.pending = {mode='freehand', points={x=coord(x-16), y=coord(y-drawing.y)}}
|
drawing.pending = {mode='freehand', points={x=coord(x-16), y=coord(y-drawing.y)}}
|
||||||
elseif current_mode == 'line' then
|
elseif current_mode == 'line' then
|
||||||
drawing.pending = {mode='line', x1=coord(x-16), y1=coord(y-drawing.y)}
|
local j = insert_point(drawing.points, coord(x-16), coord(y-drawing.y))
|
||||||
|
drawing.pending = {mode='line', p1=j}
|
||||||
end
|
end
|
||||||
lines.current = drawing
|
lines.current = drawing
|
||||||
end
|
end
|
||||||
|
@ -161,7 +170,17 @@ function propagate_to_drawings(x,y, button)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function draw_shape(left,top, shape)
|
function insert_point(points, x,y)
|
||||||
|
for i,point in ipairs(points) do
|
||||||
|
if point.x == x and point.y == y then
|
||||||
|
return i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.insert(points, {x=x, y=y})
|
||||||
|
return #points
|
||||||
|
end
|
||||||
|
|
||||||
|
function draw_shape(left,top, drawing, shape)
|
||||||
if shape.mode == 'freehand' then
|
if shape.mode == 'freehand' then
|
||||||
local prev = nil
|
local prev = nil
|
||||||
for _,point in ipairs(shape.points) do
|
for _,point in ipairs(shape.points) do
|
||||||
|
@ -171,33 +190,37 @@ function draw_shape(left,top, shape)
|
||||||
prev = point
|
prev = point
|
||||||
end
|
end
|
||||||
elseif shape.mode == 'line' then
|
elseif shape.mode == 'line' then
|
||||||
love.graphics.line(pixels(shape.x1)+left,pixels(shape.y1)+top, pixels(shape.x2)+left,pixels(shape.y2)+top)
|
local p1 = drawing.points[shape.p1]
|
||||||
|
local p2 = drawing.points[shape.p2]
|
||||||
|
love.graphics.line(pixels(p1.x)+left,pixels(p1.y)+top, pixels(p2.x)+left,pixels(p2.y)+top)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function draw_pending_shape(left,top, shape)
|
function draw_pending_shape(left,top, drawing)
|
||||||
|
local shape = drawing.pending
|
||||||
if shape.mode == 'freehand' then
|
if shape.mode == 'freehand' then
|
||||||
draw_shape(left,top, shape)
|
draw_shape(left,top, drawing, shape)
|
||||||
elseif shape.mode == 'line' then
|
elseif shape.mode == 'line' then
|
||||||
love.graphics.line(pixels(shape.x1)+left,pixels(shape.y1)+top, love.mouse.getX(),love.mouse.getY())
|
local p1 = drawing.points[shape.p1]
|
||||||
|
love.graphics.line(pixels(p1.x)+left,pixels(p1.y)+top, love.mouse.getX(),love.mouse.getY())
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_shape(x,y, shape)
|
function on_shape(x,y, drawing, shape)
|
||||||
if shape.mode == 'freehand' then
|
if shape.mode == 'freehand' then
|
||||||
return on_freehand(x,y, shape)
|
return on_freehand(x,y, drawing, shape)
|
||||||
elseif shape.mode == 'line' then
|
elseif shape.mode == 'line' then
|
||||||
return on_line(x,y, shape)
|
return on_line(x,y, drawing, shape)
|
||||||
else
|
else
|
||||||
assert(false)
|
assert(false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_freehand(x,y, shape)
|
function on_freehand(x,y, drawing, shape)
|
||||||
local prev
|
local prev
|
||||||
for _,p in ipairs(shape.points) do
|
for _,p in ipairs(shape.points) do
|
||||||
if prev then
|
if prev then
|
||||||
if on_line(x,y, {x1=prev.x,y1=prev.y, x2=p.x,y2=p.y}) then
|
if on_line(x,y, drawing, {p1=prev, p2=p}) then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -206,25 +229,33 @@ function on_freehand(x,y, shape)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_line(x,y, shape)
|
function on_line(x,y, drawing, shape)
|
||||||
if shape.x1 == shape.x2 then
|
local p1,p2
|
||||||
if math.abs(shape.x1-x) > 5 then
|
if type(shape.p1) == 'number' then
|
||||||
|
p1 = drawing.points[shape.p1]
|
||||||
|
p2 = drawing.points[shape.p2]
|
||||||
|
else
|
||||||
|
p1 = shape.p1
|
||||||
|
p2 = shape.p2
|
||||||
|
end
|
||||||
|
if p1.x == p2.x then
|
||||||
|
if math.abs(p1.x-x) > 5 then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
local y1,y2 = shape.y1,shape.y2
|
local y1,y2 = p1.y,p2.y
|
||||||
if y1 > y2 then
|
if y1 > y2 then
|
||||||
y1,y2 = y2,y1
|
y1,y2 = y2,y1
|
||||||
end
|
end
|
||||||
return y >= y1 and y <= y2
|
return y >= y1 and y <= y2
|
||||||
end
|
end
|
||||||
-- has the right slope and intercept
|
-- has the right slope and intercept
|
||||||
local m = (shape.y2 - shape.y1) / (shape.x2 - shape.x1)
|
local m = (p2.y - p1.y) / (p2.x - p1.x)
|
||||||
local yp = shape.y1 + m*(x-shape.x1)
|
local yp = p1.y + m*(x-p1.x)
|
||||||
if yp < 0.95*y or yp > 1.05*y then
|
if yp < 0.95*y or yp > 1.05*y then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
-- between endpoints
|
-- between endpoints
|
||||||
local k = (x-shape.x1) / (shape.x2-shape.x1)
|
local k = (x-p1.x) / (p2.x-p1.x)
|
||||||
return k > -0.05 and k < 1.05
|
return k > -0.05 and k < 1.05
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -259,18 +290,17 @@ function keychord_pressed(chord)
|
||||||
local drawing = current_drawing()
|
local drawing = current_drawing()
|
||||||
assert(drawing.pending.mode == 'freehand')
|
assert(drawing.pending.mode == 'freehand')
|
||||||
drawing.pending.mode = 'line'
|
drawing.pending.mode = 'line'
|
||||||
drawing.pending.x1 = drawing.pending.points[1].x
|
drawing.pending.p1 = insert_point(drawing.points, drawing.pending.points[1].x, drawing.pending.points[1].y)
|
||||||
drawing.pending.y1 = drawing.pending.points[1].y
|
|
||||||
elseif chord == 'C-l' then
|
elseif chord == 'C-l' then
|
||||||
current_mode = 'line'
|
current_mode = 'line'
|
||||||
local drawing,i,shape = select_shape_at_mouse()
|
local drawing,i,shape = select_shape_at_mouse()
|
||||||
if drawing then
|
if drawing then
|
||||||
convert_line(shape)
|
convert_line(drawing, shape)
|
||||||
end
|
end
|
||||||
elseif chord == 'C-m' then
|
elseif chord == 'C-m' then
|
||||||
local drawing,i,shape = select_shape_at_mouse()
|
local drawing,i,shape = select_shape_at_mouse()
|
||||||
if drawing then
|
if drawing then
|
||||||
convert_horvert(shape)
|
convert_horvert(drawing, shape)
|
||||||
end
|
end
|
||||||
elseif chord == 'C-s' then
|
elseif chord == 'C-s' then
|
||||||
local drawing,i,shape = select_shape_at_mouse()
|
local drawing,i,shape = select_shape_at_mouse()
|
||||||
|
@ -320,29 +350,30 @@ function select_shape_at_mouse()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function convert_line(shape)
|
function convert_line(drawing, shape)
|
||||||
-- Perhaps we should do a more sophisticated "simple linear regression"
|
-- Perhaps we should do a more sophisticated "simple linear regression"
|
||||||
-- here:
|
-- here:
|
||||||
-- https://en.wikipedia.org/wiki/Linear_regression#Simple_and_multiple_linear_regression
|
-- https://en.wikipedia.org/wiki/Linear_regression#Simple_and_multiple_linear_regression
|
||||||
-- But this works well enough for close-to-linear strokes.
|
-- But this works well enough for close-to-linear strokes.
|
||||||
assert(shape.mode == 'freehand')
|
assert(shape.mode == 'freehand')
|
||||||
shape.mode = 'line'
|
shape.mode = 'line'
|
||||||
shape.x1 = shape.points[1].x
|
shape.p1 = insert_point(drawing.points, shape.points[1].x, shape.points[1].y)
|
||||||
shape.y1 = shape.points[1].y
|
local n = #shape.points
|
||||||
shape.x2 = shape.points[#shape.points].x
|
shape.p2 = insert_point(drawing.points, shape.points[n].x, shape.points[n].y)
|
||||||
shape.y2 = shape.points[#shape.points].y
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- turn a line either horizontal or vertical
|
-- turn a line either horizontal or vertical
|
||||||
function convert_horvert(shape)
|
function convert_horvert(drawing, shape)
|
||||||
if shape.mode == 'freehand' then
|
if shape.mode == 'freehand' then
|
||||||
convert_line(shape)
|
convert_line(shape)
|
||||||
end
|
end
|
||||||
assert(shape.mode == 'line')
|
assert(shape.mode == 'line')
|
||||||
if math.abs(shape.x1-shape.x2) > math.abs(shape.y1-shape.y2) then
|
local p1 = drawing.points[shape.p1]
|
||||||
shape.y2 = shape.y1
|
local p2 = drawing.points[shape.p2]
|
||||||
|
if math.abs(p1.x-p2.x) > math.abs(p1.y-p2.y) then
|
||||||
|
p2.y = p1.y
|
||||||
else
|
else
|
||||||
shape.x2 = shape.x1
|
p2.x = p1.x
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue