2022-05-18 03:41:42 +01:00
|
|
|
-- primitives for editing drawings
|
|
|
|
Drawing = {}
|
2022-06-14 18:28:09 +01:00
|
|
|
require 'drawing_tests'
|
|
|
|
|
2022-05-20 06:02:18 +01:00
|
|
|
-- All drawings span 100% of some conceptual 'page width' and divide it up
|
|
|
|
-- into 256 parts.
|
2022-07-21 01:06:16 +01:00
|
|
|
function Drawing.draw(State, line_index, y)
|
|
|
|
local line = State.lines[line_index]
|
|
|
|
local line_cache = State.line_cache[line_index]
|
|
|
|
line_cache.starty = y
|
2022-06-12 16:26:37 +01:00
|
|
|
local pmx,pmy = App.mouse_x(), App.mouse_y()
|
2022-07-21 01:06:16 +01:00
|
|
|
if pmx < State.right and pmy > line_cache.starty and pmy < line_cache.starty+Drawing.pixels(line.h, State.width) then
|
2022-07-12 07:03:27 +01:00
|
|
|
App.color(Icon_color)
|
2022-07-21 01:06:16 +01:00
|
|
|
love.graphics.rectangle('line', State.left,line_cache.starty, State.width,Drawing.pixels(line.h, State.width))
|
2022-07-13 00:25:29 +01:00
|
|
|
if icon[State.current_drawing_mode] then
|
2022-07-21 01:06:16 +01:00
|
|
|
icon[State.current_drawing_mode](State.right-22, line_cache.starty+4)
|
2022-05-18 03:41:42 +01:00
|
|
|
else
|
2022-07-21 01:06:16 +01:00
|
|
|
icon[State.previous_drawing_mode](State.right-22, line_cache.starty+4)
|
2022-05-18 03:41:42 +01:00
|
|
|
end
|
|
|
|
|
2022-06-14 17:05:02 +01:00
|
|
|
if App.mouse_down(1) and love.keyboard.isDown('h') then
|
2022-07-22 10:29:23 +01:00
|
|
|
draw_help_with_mouse_pressed(State, line_index)
|
2022-05-18 03:41:42 +01:00
|
|
|
return
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if line.show_help then
|
2022-07-22 10:29:23 +01:00
|
|
|
draw_help_without_mouse_pressed(State, line_index)
|
2022-05-18 03:41:42 +01:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2022-07-13 15:42:38 +01:00
|
|
|
local mx = Drawing.coord(pmx-State.left, State.width)
|
2022-07-21 01:06:16 +01:00
|
|
|
local my = Drawing.coord(pmy-line_cache.starty, State.width)
|
2022-05-18 03:41:42 +01:00
|
|
|
|
|
|
|
for _,shape in ipairs(line.shapes) do
|
|
|
|
assert(shape)
|
2022-05-18 05:18:17 +01:00
|
|
|
if geom.on_shape(mx,my, line, shape) then
|
2022-07-12 07:03:27 +01:00
|
|
|
App.color(Focus_stroke_color)
|
2022-05-18 03:41:42 +01:00
|
|
|
else
|
2022-07-12 07:03:27 +01:00
|
|
|
App.color(Stroke_color)
|
2022-05-18 03:41:42 +01:00
|
|
|
end
|
2022-07-21 01:06:16 +01:00
|
|
|
Drawing.draw_shape(line, shape, line_cache.starty, State.left,State.right)
|
2022-05-18 03:41:42 +01:00
|
|
|
end
|
2022-07-13 16:02:41 +01:00
|
|
|
|
2022-07-13 16:04:06 +01:00
|
|
|
local function px(x) return Drawing.pixels(x, State.width)+State.left end
|
2022-07-21 01:06:16 +01:00
|
|
|
local function py(y) return Drawing.pixels(y, State.width)+line_cache.starty end
|
2022-05-21 22:18:31 +01:00
|
|
|
for i,p in ipairs(line.points) do
|
2022-05-18 03:41:42 +01:00
|
|
|
if p.deleted == nil then
|
2022-07-13 05:58:46 +01:00
|
|
|
if Drawing.near(p, mx,my, State.width) then
|
2022-07-12 07:03:27 +01:00
|
|
|
App.color(Focus_stroke_color)
|
2022-07-13 16:02:41 +01:00
|
|
|
love.graphics.circle('line', px(p.x),py(p.y), Same_point_distance)
|
2022-05-18 03:41:42 +01:00
|
|
|
else
|
2022-07-12 07:03:27 +01:00
|
|
|
App.color(Stroke_color)
|
2022-07-13 16:02:41 +01:00
|
|
|
love.graphics.circle('fill', px(p.x),py(p.y), 2)
|
2022-05-18 03:41:42 +01:00
|
|
|
end
|
2022-05-21 22:03:06 +01:00
|
|
|
if p.name then
|
2022-06-14 18:50:06 +01:00
|
|
|
-- TODO: clip
|
2022-07-13 16:02:41 +01:00
|
|
|
local x,y = px(p.x)+5, py(p.y)+5
|
2022-06-03 07:36:07 +01:00
|
|
|
love.graphics.print(p.name, x,y)
|
2022-07-13 00:25:29 +01:00
|
|
|
if State.current_drawing_mode == 'name' and i == line.pending.target_point then
|
2022-05-21 22:18:31 +01:00
|
|
|
-- create a faint red box for the name
|
2022-07-12 07:03:27 +01:00
|
|
|
App.color(Current_name_background_color)
|
2023-04-02 00:29:17 +01:00
|
|
|
local name_width
|
2022-05-21 22:18:31 +01:00
|
|
|
if p.name == '' then
|
2023-04-02 00:29:17 +01:00
|
|
|
name_width = App.width('m')
|
2022-05-21 22:18:31 +01:00
|
|
|
else
|
2023-04-02 00:29:17 +01:00
|
|
|
name_width = App.width(p.name)
|
2022-05-21 22:18:31 +01:00
|
|
|
end
|
2023-04-02 00:29:17 +01:00
|
|
|
love.graphics.rectangle('fill', x,y, name_width, State.line_height)
|
2022-05-21 22:18:31 +01:00
|
|
|
end
|
2022-05-21 22:03:06 +01:00
|
|
|
end
|
2022-05-18 03:41:42 +01:00
|
|
|
end
|
|
|
|
end
|
2022-07-12 07:03:27 +01:00
|
|
|
App.color(Current_stroke_color)
|
2022-07-21 01:06:16 +01:00
|
|
|
Drawing.draw_pending_shape(line, line_cache.starty, State.left,State.right)
|
2022-05-18 05:18:17 +01:00
|
|
|
end
|
|
|
|
|
2022-07-13 05:58:46 +01:00
|
|
|
function Drawing.draw_shape(drawing, shape, top, left,right)
|
|
|
|
local width = right-left
|
2022-07-13 16:04:06 +01:00
|
|
|
local function px(x) return Drawing.pixels(x, width)+left end
|
|
|
|
local function py(y) return Drawing.pixels(y, width)+top end
|
2022-05-18 15:21:50 +01:00
|
|
|
if shape.mode == 'freehand' then
|
|
|
|
local prev = nil
|
|
|
|
for _,point in ipairs(shape.points) do
|
|
|
|
if prev then
|
2022-07-13 15:42:38 +01:00
|
|
|
love.graphics.line(px(prev.x),py(prev.y), px(point.x),py(point.y))
|
2022-05-18 15:21:50 +01:00
|
|
|
end
|
|
|
|
prev = point
|
|
|
|
end
|
|
|
|
elseif shape.mode == 'line' or shape.mode == 'manhattan' then
|
|
|
|
local p1 = drawing.points[shape.p1]
|
|
|
|
local p2 = drawing.points[shape.p2]
|
2022-07-13 15:42:38 +01:00
|
|
|
love.graphics.line(px(p1.x),py(p1.y), px(p2.x),py(p2.y))
|
2022-05-18 23:29:18 +01:00
|
|
|
elseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' then
|
2022-05-18 15:21:50 +01:00
|
|
|
local prev = nil
|
|
|
|
for _,point in ipairs(shape.vertices) do
|
|
|
|
local curr = drawing.points[point]
|
|
|
|
if prev then
|
2022-07-13 15:42:38 +01:00
|
|
|
love.graphics.line(px(prev.x),py(prev.y), px(curr.x),py(curr.y))
|
2022-05-18 15:21:50 +01:00
|
|
|
end
|
|
|
|
prev = curr
|
|
|
|
end
|
|
|
|
-- close the loop
|
|
|
|
local curr = drawing.points[shape.vertices[1]]
|
2022-07-13 15:42:38 +01:00
|
|
|
love.graphics.line(px(prev.x),py(prev.y), px(curr.x),py(curr.y))
|
2022-05-18 15:21:50 +01:00
|
|
|
elseif shape.mode == 'circle' then
|
2022-06-14 18:50:06 +01:00
|
|
|
-- TODO: clip
|
2022-05-18 15:21:50 +01:00
|
|
|
local center = drawing.points[shape.center]
|
2022-07-13 15:42:38 +01:00
|
|
|
love.graphics.circle('line', px(center.x),py(center.y), Drawing.pixels(shape.radius, width))
|
2022-05-18 15:21:50 +01:00
|
|
|
elseif shape.mode == 'arc' then
|
|
|
|
local center = drawing.points[shape.center]
|
2022-07-13 15:42:38 +01:00
|
|
|
love.graphics.arc('line', 'open', px(center.x),py(center.y), Drawing.pixels(shape.radius, width), shape.start_angle, shape.end_angle, 360)
|
2022-05-18 15:21:50 +01:00
|
|
|
elseif shape.mode == 'deleted' then
|
2022-05-18 23:29:18 +01:00
|
|
|
-- ignore
|
2022-05-18 15:21:50 +01:00
|
|
|
else
|
|
|
|
print(shape.mode)
|
|
|
|
assert(false)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-07-13 05:58:46 +01:00
|
|
|
function Drawing.draw_pending_shape(drawing, top, left,right)
|
|
|
|
local width = right-left
|
2022-07-13 15:42:38 +01:00
|
|
|
local pmx,pmy = App.mouse_x(), App.mouse_y()
|
2022-07-13 16:04:06 +01:00
|
|
|
local function px(x) return Drawing.pixels(x, width)+left end
|
|
|
|
local function py(y) return Drawing.pixels(y, width)+top end
|
2022-07-13 15:42:38 +01:00
|
|
|
local mx = Drawing.coord(pmx-left, width)
|
|
|
|
local my = Drawing.coord(pmy-top, width)
|
2022-08-04 02:40:07 +01:00
|
|
|
-- recreate pixels from coords to precisely mimic how the drawing will look
|
2022-12-24 01:16:19 +00:00
|
|
|
-- after mouse_release
|
2022-08-04 02:40:07 +01:00
|
|
|
pmx,pmy = px(mx), py(my)
|
2022-05-18 15:21:50 +01:00
|
|
|
local shape = drawing.pending
|
2022-05-18 23:29:18 +01:00
|
|
|
if shape.mode == nil then
|
|
|
|
-- nothing pending
|
|
|
|
elseif shape.mode == 'freehand' then
|
2022-07-05 19:32:45 +01:00
|
|
|
local shape_copy = deepcopy(shape)
|
|
|
|
Drawing.smoothen(shape_copy)
|
2022-07-13 05:58:46 +01:00
|
|
|
Drawing.draw_shape(drawing, shape_copy, top, left,right)
|
2022-05-18 15:21:50 +01:00
|
|
|
elseif shape.mode == 'line' then
|
|
|
|
if mx < 0 or mx >= 256 or my < 0 or my >= drawing.h then
|
|
|
|
return
|
|
|
|
end
|
2022-05-18 15:34:18 +01:00
|
|
|
local p1 = drawing.points[shape.p1]
|
2022-07-13 15:42:38 +01:00
|
|
|
love.graphics.line(px(p1.x),py(p1.y), pmx,pmy)
|
2022-05-18 15:21:50 +01:00
|
|
|
elseif shape.mode == 'manhattan' then
|
|
|
|
if mx < 0 or mx >= 256 or my < 0 or my >= drawing.h then
|
|
|
|
return
|
|
|
|
end
|
2022-05-18 15:34:18 +01:00
|
|
|
local p1 = drawing.points[shape.p1]
|
2022-05-18 15:21:50 +01:00
|
|
|
if math.abs(mx-p1.x) > math.abs(my-p1.y) then
|
2022-07-13 15:42:38 +01:00
|
|
|
love.graphics.line(px(p1.x),py(p1.y), pmx, py(p1.y))
|
2022-05-18 15:21:50 +01:00
|
|
|
else
|
2022-07-13 15:42:38 +01:00
|
|
|
love.graphics.line(px(p1.x),py(p1.y), px(p1.x),pmy)
|
2022-05-18 15:21:50 +01:00
|
|
|
end
|
|
|
|
elseif shape.mode == 'polygon' then
|
|
|
|
-- don't close the loop on a pending polygon
|
|
|
|
local prev = nil
|
|
|
|
for _,point in ipairs(shape.vertices) do
|
|
|
|
local curr = drawing.points[point]
|
|
|
|
if prev then
|
2022-07-13 15:42:38 +01:00
|
|
|
love.graphics.line(px(prev.x),py(prev.y), px(curr.x),py(curr.y))
|
2022-05-18 15:21:50 +01:00
|
|
|
end
|
|
|
|
prev = curr
|
|
|
|
end
|
2022-07-13 15:42:38 +01:00
|
|
|
love.graphics.line(px(prev.x),py(prev.y), pmx,pmy)
|
2022-05-18 23:29:18 +01:00
|
|
|
elseif shape.mode == 'rectangle' then
|
|
|
|
local first = drawing.points[shape.vertices[1]]
|
|
|
|
if #shape.vertices == 1 then
|
2022-07-13 15:42:38 +01:00
|
|
|
love.graphics.line(px(first.x),py(first.y), pmx,pmy)
|
2022-05-18 23:29:18 +01:00
|
|
|
return
|
|
|
|
end
|
|
|
|
local second = drawing.points[shape.vertices[2]]
|
|
|
|
local thirdx,thirdy, fourthx,fourthy = Drawing.complete_rectangle(first.x,first.y, second.x,second.y, mx,my)
|
2022-07-13 15:42:38 +01:00
|
|
|
love.graphics.line(px(first.x),py(first.y), px(second.x),py(second.y))
|
|
|
|
love.graphics.line(px(second.x),py(second.y), px(thirdx),py(thirdy))
|
|
|
|
love.graphics.line(px(thirdx),py(thirdy), px(fourthx),py(fourthy))
|
|
|
|
love.graphics.line(px(fourthx),py(fourthy), px(first.x),py(first.y))
|
2022-05-18 23:29:18 +01:00
|
|
|
elseif shape.mode == 'square' then
|
|
|
|
local first = drawing.points[shape.vertices[1]]
|
|
|
|
if #shape.vertices == 1 then
|
2022-07-13 15:42:38 +01:00
|
|
|
love.graphics.line(px(first.x),py(first.y), pmx,pmy)
|
2022-05-18 23:29:18 +01:00
|
|
|
return
|
|
|
|
end
|
|
|
|
local second = drawing.points[shape.vertices[2]]
|
|
|
|
local thirdx,thirdy, fourthx,fourthy = Drawing.complete_square(first.x,first.y, second.x,second.y, mx,my)
|
2022-07-13 15:42:38 +01:00
|
|
|
love.graphics.line(px(first.x),py(first.y), px(second.x),py(second.y))
|
|
|
|
love.graphics.line(px(second.x),py(second.y), px(thirdx),py(thirdy))
|
|
|
|
love.graphics.line(px(thirdx),py(thirdy), px(fourthx),py(fourthy))
|
|
|
|
love.graphics.line(px(fourthx),py(fourthy), px(first.x),py(first.y))
|
2022-05-18 15:21:50 +01:00
|
|
|
elseif shape.mode == 'circle' then
|
|
|
|
local center = drawing.points[shape.center]
|
|
|
|
if mx < 0 or mx >= 256 or my < 0 or my >= drawing.h then
|
|
|
|
return
|
|
|
|
end
|
2023-09-15 16:24:54 +01:00
|
|
|
local r = round(geom.dist(center.x, center.y, mx, my))
|
2022-07-13 15:42:38 +01:00
|
|
|
local cx,cy = px(center.x), py(center.y)
|
2023-09-15 16:24:54 +01:00
|
|
|
love.graphics.circle('line', cx,cy, Drawing.pixels(r, width))
|
2022-05-18 15:21:50 +01:00
|
|
|
elseif shape.mode == 'arc' then
|
|
|
|
local center = drawing.points[shape.center]
|
|
|
|
if mx < 0 or mx >= 256 or my < 0 or my >= drawing.h then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
shape.end_angle = geom.angle_with_hint(center.x,center.y, mx,my, shape.end_angle)
|
2022-07-13 15:42:38 +01:00
|
|
|
local cx,cy = px(center.x), py(center.y)
|
2022-07-13 05:58:46 +01:00
|
|
|
love.graphics.arc('line', 'open', cx,cy, Drawing.pixels(shape.radius, width), shape.start_angle, shape.end_angle, 360)
|
2022-05-21 16:40:15 +01:00
|
|
|
elseif shape.mode == 'move' then
|
|
|
|
-- nothing pending; changes are immediately committed
|
2022-05-21 22:03:06 +01:00
|
|
|
elseif shape.mode == 'name' then
|
|
|
|
-- nothing pending; changes are immediately committed
|
2022-05-18 23:29:18 +01:00
|
|
|
else
|
|
|
|
print(shape.mode)
|
|
|
|
assert(false)
|
2022-05-18 15:21:50 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-07-21 01:06:16 +01:00
|
|
|
function Drawing.in_drawing(drawing, line_cache, x,y, left,right)
|
|
|
|
if line_cache.starty == nil then return false end -- outside current page
|
2022-07-13 05:58:46 +01:00
|
|
|
local width = right-left
|
2022-07-21 01:06:16 +01:00
|
|
|
return y >= line_cache.starty and y < line_cache.starty + Drawing.pixels(drawing.h, width) and x >= left and x < right
|
2022-05-18 06:53:09 +01:00
|
|
|
end
|
|
|
|
|
2022-12-24 03:26:05 +00:00
|
|
|
function Drawing.mouse_press(State, drawing_index, x,y, mouse_button)
|
2022-07-21 01:06:16 +01:00
|
|
|
local drawing = State.lines[drawing_index]
|
|
|
|
local line_cache = State.line_cache[drawing_index]
|
2022-07-13 16:14:01 +01:00
|
|
|
local cx = Drawing.coord(x-State.left, State.width)
|
2022-07-21 01:06:16 +01:00
|
|
|
local cy = Drawing.coord(y-line_cache.starty, State.width)
|
2022-07-13 00:20:25 +01:00
|
|
|
if State.current_drawing_mode == 'freehand' then
|
2022-07-13 16:14:01 +01:00
|
|
|
drawing.pending = {mode=State.current_drawing_mode, points={{x=cx, y=cy}}}
|
2022-07-13 00:20:25 +01:00
|
|
|
elseif State.current_drawing_mode == 'line' or State.current_drawing_mode == 'manhattan' then
|
2022-07-13 16:14:01 +01:00
|
|
|
local j = Drawing.find_or_insert_point(drawing.points, cx, cy, State.width)
|
2022-07-13 00:20:25 +01:00
|
|
|
drawing.pending = {mode=State.current_drawing_mode, p1=j}
|
|
|
|
elseif State.current_drawing_mode == 'polygon' or State.current_drawing_mode == 'rectangle' or State.current_drawing_mode == 'square' then
|
2022-07-13 16:14:01 +01:00
|
|
|
local j = Drawing.find_or_insert_point(drawing.points, cx, cy, State.width)
|
2022-07-13 00:20:25 +01:00
|
|
|
drawing.pending = {mode=State.current_drawing_mode, vertices={j}}
|
|
|
|
elseif State.current_drawing_mode == 'circle' then
|
2022-07-13 16:14:01 +01:00
|
|
|
local j = Drawing.find_or_insert_point(drawing.points, cx, cy, State.width)
|
2022-07-13 00:20:25 +01:00
|
|
|
drawing.pending = {mode=State.current_drawing_mode, center=j}
|
|
|
|
elseif State.current_drawing_mode == 'move' then
|
2022-12-24 01:16:19 +00:00
|
|
|
-- all the action is in mouse_release
|
2022-07-13 00:20:25 +01:00
|
|
|
elseif State.current_drawing_mode == 'name' then
|
2022-05-31 00:23:08 +01:00
|
|
|
-- nothing
|
2022-05-18 23:29:18 +01:00
|
|
|
else
|
2022-07-13 00:20:25 +01:00
|
|
|
print(State.current_drawing_mode)
|
2022-05-18 23:29:18 +01:00
|
|
|
assert(false)
|
2022-05-18 06:53:09 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-05-18 07:16:08 +01:00
|
|
|
-- a couple of operations on drawings need to constantly check the state of the mouse
|
2022-07-13 00:26:00 +01:00
|
|
|
function Drawing.update(State)
|
|
|
|
if State.lines.current_drawing == nil then return end
|
|
|
|
local drawing = State.lines.current_drawing
|
2022-07-21 01:06:16 +01:00
|
|
|
local line_cache = State.line_cache[State.lines.current_drawing_index]
|
assume starty can be nil in update
This is a backport of a bugfix in pensieve.love. It's not _technically_
a bug here in lines.love, but it seems worth establishing an
architectural invariant (or rather lack of invariant).
LÖVE's standard event loop performs the following sequence of operations
in a single frame:
* process events
* update
* draw
Ideally any mutations to global state happen during the first two
phases, while drawing includes no mutation.
However, there is a special case: `starty`, the top y coordinate for
each each line in the editor. This is used all over the place, and the
cheapest way to compute it is to simply save it while drawing.
However, draw by definition only updates `starty` for lines that are
drawn on screen. To avoid stale data on lines off screen, say after
scrolling, events often clear `starty` for all lines, leaving it to the
next draw phase to repopulate the right lines.
Sandwiched between the above two "however"s, the update phase needs to
gracefully handle `starty` being nil in the occasional frame right after
an event.
I think I've audited all our uses of `starty`, and this commit fixes the
only place that violates this rule.
2023-09-15 19:13:04 +01:00
|
|
|
if line_cache.starty == nil then
|
|
|
|
-- some event cleared starty just this frame
|
|
|
|
-- draw in this frame will soon set starty
|
|
|
|
-- just skip this frame
|
|
|
|
return
|
|
|
|
end
|
2022-05-18 07:16:08 +01:00
|
|
|
assert(drawing.mode == 'drawing')
|
2022-07-13 16:19:06 +01:00
|
|
|
local pmx, pmy = App.mouse_x(), App.mouse_y()
|
2022-07-13 16:21:07 +01:00
|
|
|
local mx = Drawing.coord(pmx-State.left, State.width)
|
2022-07-21 01:06:16 +01:00
|
|
|
local my = Drawing.coord(pmy-line_cache.starty, State.width)
|
2022-06-14 17:05:02 +01:00
|
|
|
if App.mouse_down(1) then
|
2022-07-21 01:06:16 +01:00
|
|
|
if Drawing.in_drawing(drawing, line_cache, pmx,pmy, State.left,State.right) then
|
2022-05-18 07:16:08 +01:00
|
|
|
if drawing.pending.mode == 'freehand' then
|
2022-07-13 16:21:07 +01:00
|
|
|
table.insert(drawing.pending.points, {x=mx, y=my})
|
2022-05-18 07:16:08 +01:00
|
|
|
elseif drawing.pending.mode == 'move' then
|
|
|
|
drawing.pending.target_point.x = mx
|
|
|
|
drawing.pending.target_point.y = my
|
2022-06-18 06:52:34 +01:00
|
|
|
Drawing.relax_constraints(drawing, drawing.pending.target_point_index)
|
2022-05-18 07:16:08 +01:00
|
|
|
end
|
|
|
|
end
|
2022-07-13 00:26:00 +01:00
|
|
|
elseif State.current_drawing_mode == 'move' then
|
2022-07-21 01:06:16 +01:00
|
|
|
if Drawing.in_drawing(drawing, line_cache, pmx, pmy, State.left,State.right) then
|
2022-05-18 07:16:08 +01:00
|
|
|
drawing.pending.target_point.x = mx
|
|
|
|
drawing.pending.target_point.y = my
|
2022-06-18 06:52:34 +01:00
|
|
|
Drawing.relax_constraints(drawing, drawing.pending.target_point_index)
|
2022-05-18 07:16:08 +01:00
|
|
|
end
|
2022-05-18 23:29:18 +01:00
|
|
|
else
|
2022-05-21 16:40:15 +01:00
|
|
|
-- do nothing
|
2022-05-18 07:16:08 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-06-18 06:52:34 +01:00
|
|
|
function Drawing.relax_constraints(drawing, p)
|
|
|
|
for _,shape in ipairs(drawing.shapes) do
|
|
|
|
if shape.mode == 'manhattan' then
|
|
|
|
if shape.p1 == p then
|
|
|
|
shape.mode = 'line'
|
|
|
|
elseif shape.p2 == p then
|
|
|
|
shape.mode = 'line'
|
|
|
|
end
|
|
|
|
elseif shape.mode == 'rectangle' or shape.mode == 'square' then
|
|
|
|
for _,v in ipairs(shape.vertices) do
|
|
|
|
if v == p then
|
|
|
|
shape.mode = 'polygon'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-12-24 01:16:19 +00:00
|
|
|
function Drawing.mouse_release(State, x,y, mouse_button)
|
2022-07-13 00:21:56 +01:00
|
|
|
if State.current_drawing_mode == 'move' then
|
|
|
|
State.current_drawing_mode = State.previous_drawing_mode
|
|
|
|
State.previous_drawing_mode = nil
|
|
|
|
if State.lines.current_drawing then
|
|
|
|
State.lines.current_drawing.pending = {}
|
|
|
|
State.lines.current_drawing = nil
|
2022-07-12 22:18:50 +01:00
|
|
|
end
|
2022-07-13 00:21:56 +01:00
|
|
|
elseif State.lines.current_drawing then
|
|
|
|
local drawing = State.lines.current_drawing
|
2022-07-21 01:06:16 +01:00
|
|
|
local line_cache = State.line_cache[State.lines.current_drawing_index]
|
2022-06-03 16:00:31 +01:00
|
|
|
if drawing.pending then
|
2022-07-05 19:04:39 +01:00
|
|
|
if drawing.pending.mode == nil then
|
|
|
|
-- nothing pending
|
|
|
|
elseif drawing.pending.mode == 'freehand' then
|
2022-05-18 06:55:45 +01:00
|
|
|
-- the last point added during update is good enough
|
2022-07-05 19:32:45 +01:00
|
|
|
Drawing.smoothen(drawing.pending)
|
2022-06-03 16:00:31 +01:00
|
|
|
table.insert(drawing.shapes, drawing.pending)
|
|
|
|
elseif drawing.pending.mode == 'line' then
|
2022-07-21 01:06:16 +01:00
|
|
|
local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width)
|
2022-06-03 16:00:31 +01:00
|
|
|
if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h then
|
2022-07-13 05:58:46 +01:00
|
|
|
drawing.pending.p2 = Drawing.find_or_insert_point(drawing.points, mx,my, State.width)
|
2022-06-03 16:00:31 +01:00
|
|
|
table.insert(drawing.shapes, drawing.pending)
|
2022-05-18 06:55:45 +01:00
|
|
|
end
|
2022-06-03 16:00:31 +01:00
|
|
|
elseif drawing.pending.mode == 'manhattan' then
|
|
|
|
local p1 = drawing.points[drawing.pending.p1]
|
2022-07-21 01:06:16 +01:00
|
|
|
local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width)
|
2022-06-03 16:00:31 +01:00
|
|
|
if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h then
|
2022-05-18 06:55:45 +01:00
|
|
|
if math.abs(mx-p1.x) > math.abs(my-p1.y) then
|
2022-07-13 05:58:46 +01:00
|
|
|
drawing.pending.p2 = Drawing.find_or_insert_point(drawing.points, mx, p1.y, State.width)
|
2022-05-18 06:55:45 +01:00
|
|
|
else
|
2022-07-13 05:58:46 +01:00
|
|
|
drawing.pending.p2 = Drawing.find_or_insert_point(drawing.points, p1.x, my, State.width)
|
2022-05-18 06:55:45 +01:00
|
|
|
end
|
2022-06-03 16:00:31 +01:00
|
|
|
local p2 = drawing.points[drawing.pending.p2]
|
2022-07-21 01:06:16 +01:00
|
|
|
App.mouse_move(State.left+Drawing.pixels(p2.x, State.width), line_cache.starty+Drawing.pixels(p2.y, State.width))
|
2022-06-03 16:00:31 +01:00
|
|
|
table.insert(drawing.shapes, drawing.pending)
|
2022-05-18 06:55:45 +01:00
|
|
|
end
|
2022-06-03 16:00:31 +01:00
|
|
|
elseif drawing.pending.mode == 'polygon' then
|
2022-07-21 01:06:16 +01:00
|
|
|
local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width)
|
2022-06-03 16:00:31 +01:00
|
|
|
if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h then
|
2022-07-13 05:58:46 +01:00
|
|
|
table.insert(drawing.pending.vertices, Drawing.find_or_insert_point(drawing.points, mx,my, State.width))
|
2022-06-03 16:00:31 +01:00
|
|
|
table.insert(drawing.shapes, drawing.pending)
|
2022-05-18 06:55:45 +01:00
|
|
|
end
|
2022-06-03 16:00:31 +01:00
|
|
|
elseif drawing.pending.mode == 'rectangle' then
|
|
|
|
assert(#drawing.pending.vertices <= 2)
|
|
|
|
if #drawing.pending.vertices == 2 then
|
2022-07-21 01:06:16 +01:00
|
|
|
local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width)
|
2022-06-03 16:00:31 +01:00
|
|
|
if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h then
|
|
|
|
local first = drawing.points[drawing.pending.vertices[1]]
|
|
|
|
local second = drawing.points[drawing.pending.vertices[2]]
|
2022-05-18 23:29:18 +01:00
|
|
|
local thirdx,thirdy, fourthx,fourthy = Drawing.complete_rectangle(first.x,first.y, second.x,second.y, mx,my)
|
2022-07-13 05:58:46 +01:00
|
|
|
table.insert(drawing.pending.vertices, Drawing.find_or_insert_point(drawing.points, thirdx,thirdy, State.width))
|
|
|
|
table.insert(drawing.pending.vertices, Drawing.find_or_insert_point(drawing.points, fourthx,fourthy, State.width))
|
2022-06-03 16:00:31 +01:00
|
|
|
table.insert(drawing.shapes, drawing.pending)
|
2022-05-18 23:29:18 +01:00
|
|
|
end
|
|
|
|
else
|
|
|
|
-- too few points; draw nothing
|
|
|
|
end
|
2022-06-03 16:00:31 +01:00
|
|
|
elseif drawing.pending.mode == 'square' then
|
|
|
|
assert(#drawing.pending.vertices <= 2)
|
|
|
|
if #drawing.pending.vertices == 2 then
|
2022-07-21 01:06:16 +01:00
|
|
|
local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width)
|
2022-06-03 16:00:31 +01:00
|
|
|
if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h then
|
|
|
|
local first = drawing.points[drawing.pending.vertices[1]]
|
|
|
|
local second = drawing.points[drawing.pending.vertices[2]]
|
2022-05-18 23:29:18 +01:00
|
|
|
local thirdx,thirdy, fourthx,fourthy = Drawing.complete_square(first.x,first.y, second.x,second.y, mx,my)
|
2022-07-13 05:58:46 +01:00
|
|
|
table.insert(drawing.pending.vertices, Drawing.find_or_insert_point(drawing.points, thirdx,thirdy, State.width))
|
|
|
|
table.insert(drawing.pending.vertices, Drawing.find_or_insert_point(drawing.points, fourthx,fourthy, State.width))
|
2022-06-03 16:00:31 +01:00
|
|
|
table.insert(drawing.shapes, drawing.pending)
|
2022-05-18 23:29:18 +01:00
|
|
|
end
|
|
|
|
end
|
2022-06-03 16:00:31 +01:00
|
|
|
elseif drawing.pending.mode == 'circle' then
|
2022-07-21 01:06:16 +01:00
|
|
|
local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width)
|
2022-06-03 16:00:31 +01:00
|
|
|
if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h then
|
|
|
|
local center = drawing.points[drawing.pending.center]
|
2022-07-30 05:54:06 +01:00
|
|
|
drawing.pending.radius = round(geom.dist(center.x,center.y, mx,my))
|
2022-06-03 16:00:31 +01:00
|
|
|
table.insert(drawing.shapes, drawing.pending)
|
2022-05-18 06:55:45 +01:00
|
|
|
end
|
2022-06-03 16:00:31 +01:00
|
|
|
elseif drawing.pending.mode == 'arc' then
|
2022-07-21 01:06:16 +01:00
|
|
|
local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width)
|
2022-06-03 16:00:31 +01:00
|
|
|
if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h then
|
|
|
|
local center = drawing.points[drawing.pending.center]
|
|
|
|
drawing.pending.end_angle = geom.angle_with_hint(center.x,center.y, mx,my, drawing.pending.end_angle)
|
|
|
|
table.insert(drawing.shapes, drawing.pending)
|
2022-05-18 06:55:45 +01:00
|
|
|
end
|
2022-06-03 16:00:31 +01:00
|
|
|
elseif drawing.pending.mode == 'name' then
|
2022-05-31 00:23:08 +01:00
|
|
|
-- drop it
|
2022-05-18 23:29:18 +01:00
|
|
|
else
|
2022-06-03 16:00:31 +01:00
|
|
|
print(drawing.pending.mode)
|
2022-05-18 23:29:18 +01:00
|
|
|
assert(false)
|
2022-05-18 06:55:45 +01:00
|
|
|
end
|
2022-07-13 00:21:56 +01:00
|
|
|
State.lines.current_drawing.pending = {}
|
|
|
|
State.lines.current_drawing = nil
|
2022-05-18 06:55:45 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-12-24 01:16:19 +00:00
|
|
|
function Drawing.keychord_press(State, chord)
|
2022-06-14 17:05:02 +01:00
|
|
|
if chord == 'C-p' and not App.mouse_down(1) then
|
2022-07-13 00:21:03 +01:00
|
|
|
State.current_drawing_mode = 'freehand'
|
2022-06-15 19:10:30 +01:00
|
|
|
elseif App.mouse_down(1) and chord == 'l' then
|
2022-07-13 00:21:03 +01:00
|
|
|
State.current_drawing_mode = 'line'
|
2022-07-13 01:27:00 +01:00
|
|
|
local _,drawing = Drawing.current_drawing(State)
|
2022-06-15 19:10:30 +01:00
|
|
|
if drawing.pending.mode == 'freehand' then
|
2022-07-13 05:58:46 +01:00
|
|
|
drawing.pending.p1 = Drawing.find_or_insert_point(drawing.points, drawing.pending.points[1].x, drawing.pending.points[1].y, State.width)
|
2022-06-15 19:10:30 +01:00
|
|
|
elseif drawing.pending.mode == 'polygon' or drawing.pending.mode == 'rectangle' or drawing.pending.mode == 'square' then
|
|
|
|
drawing.pending.p1 = drawing.pending.vertices[1]
|
|
|
|
elseif drawing.pending.mode == 'circle' or drawing.pending.mode == 'arc' then
|
|
|
|
drawing.pending.p1 = drawing.pending.center
|
|
|
|
end
|
|
|
|
drawing.pending.mode = 'line'
|
|
|
|
elseif chord == 'C-l' and not App.mouse_down(1) then
|
2022-07-13 00:21:03 +01:00
|
|
|
State.current_drawing_mode = 'line'
|
2022-06-15 19:10:30 +01:00
|
|
|
elseif App.mouse_down(1) and chord == 'm' then
|
2022-07-13 00:21:03 +01:00
|
|
|
State.current_drawing_mode = 'manhattan'
|
2022-07-13 01:27:00 +01:00
|
|
|
local drawing = Drawing.select_drawing_at_mouse(State)
|
2022-06-15 19:10:30 +01:00
|
|
|
if drawing.pending.mode == 'freehand' then
|
2022-07-13 05:58:46 +01:00
|
|
|
drawing.pending.p1 = Drawing.find_or_insert_point(drawing.points, drawing.pending.points[1].x, drawing.pending.points[1].y, State.width)
|
2022-06-15 19:10:30 +01:00
|
|
|
elseif drawing.pending.mode == 'line' then
|
|
|
|
-- do nothing
|
|
|
|
elseif drawing.pending.mode == 'polygon' or drawing.pending.mode == 'rectangle' or drawing.pending.mode == 'square' then
|
|
|
|
drawing.pending.p1 = drawing.pending.vertices[1]
|
|
|
|
elseif drawing.pending.mode == 'circle' or drawing.pending.mode == 'arc' then
|
|
|
|
drawing.pending.p1 = drawing.pending.center
|
|
|
|
end
|
|
|
|
drawing.pending.mode = 'manhattan'
|
|
|
|
elseif chord == 'C-m' and not App.mouse_down(1) then
|
2022-07-13 00:21:03 +01:00
|
|
|
State.current_drawing_mode = 'manhattan'
|
2022-06-14 17:05:02 +01:00
|
|
|
elseif chord == 'C-g' and not App.mouse_down(1) then
|
2022-07-13 00:21:03 +01:00
|
|
|
State.current_drawing_mode = 'polygon'
|
2022-06-14 17:05:02 +01:00
|
|
|
elseif App.mouse_down(1) and chord == 'g' then
|
2022-07-13 00:21:03 +01:00
|
|
|
State.current_drawing_mode = 'polygon'
|
2022-07-13 01:27:00 +01:00
|
|
|
local _,drawing = Drawing.current_drawing(State)
|
2022-05-18 06:36:10 +01:00
|
|
|
if drawing.pending.mode == 'freehand' then
|
2022-07-13 05:58:46 +01:00
|
|
|
drawing.pending.vertices = {Drawing.find_or_insert_point(drawing.points, drawing.pending.points[1].x, drawing.pending.points[1].y, State.width)}
|
2022-05-18 06:36:10 +01:00
|
|
|
elseif drawing.pending.mode == 'line' or drawing.pending.mode == 'manhattan' then
|
|
|
|
if drawing.pending.vertices == nil then
|
|
|
|
drawing.pending.vertices = {drawing.pending.p1}
|
|
|
|
end
|
2022-06-15 16:53:34 +01:00
|
|
|
elseif drawing.pending.mode == 'rectangle' or drawing.pending.mode == 'square' then
|
2022-05-21 17:30:02 +01:00
|
|
|
-- reuse existing vertices
|
2022-05-18 06:36:10 +01:00
|
|
|
elseif drawing.pending.mode == 'circle' or drawing.pending.mode == 'arc' then
|
|
|
|
drawing.pending.vertices = {drawing.pending.center}
|
|
|
|
end
|
|
|
|
drawing.pending.mode = 'polygon'
|
2022-06-14 17:05:02 +01:00
|
|
|
elseif chord == 'C-r' and not App.mouse_down(1) then
|
2022-07-13 00:21:03 +01:00
|
|
|
State.current_drawing_mode = 'rectangle'
|
2022-06-14 17:05:02 +01:00
|
|
|
elseif App.mouse_down(1) and chord == 'r' then
|
2022-07-13 00:21:03 +01:00
|
|
|
State.current_drawing_mode = 'rectangle'
|
2022-07-13 01:27:00 +01:00
|
|
|
local _,drawing = Drawing.current_drawing(State)
|
2022-05-18 23:29:18 +01:00
|
|
|
if drawing.pending.mode == 'freehand' then
|
2022-07-13 05:58:46 +01:00
|
|
|
drawing.pending.vertices = {Drawing.find_or_insert_point(drawing.points, drawing.pending.points[1].x, drawing.pending.points[1].y, State.width)}
|
2022-05-18 23:29:18 +01:00
|
|
|
elseif drawing.pending.mode == 'line' or drawing.pending.mode == 'manhattan' then
|
|
|
|
if drawing.pending.vertices == nil then
|
|
|
|
drawing.pending.vertices = {drawing.pending.p1}
|
|
|
|
end
|
2022-05-26 01:07:19 +01:00
|
|
|
elseif drawing.pending.mode == 'polygon' or drawing.pending.mode == 'square' then
|
|
|
|
-- reuse existing (1-2) vertices
|
2022-06-15 16:53:34 +01:00
|
|
|
elseif drawing.pending.mode == 'circle' or drawing.pending.mode == 'arc' then
|
|
|
|
drawing.pending.vertices = {drawing.pending.center}
|
2022-05-18 23:29:18 +01:00
|
|
|
end
|
|
|
|
drawing.pending.mode = 'rectangle'
|
2022-06-14 17:05:02 +01:00
|
|
|
elseif chord == 'C-s' and not App.mouse_down(1) then
|
2022-07-13 00:21:03 +01:00
|
|
|
State.current_drawing_mode = 'square'
|
2022-06-14 17:05:02 +01:00
|
|
|
elseif App.mouse_down(1) and chord == 's' then
|
2022-07-13 00:21:03 +01:00
|
|
|
State.current_drawing_mode = 'square'
|
2022-07-13 01:27:00 +01:00
|
|
|
local _,drawing = Drawing.current_drawing(State)
|
2022-05-18 23:29:18 +01:00
|
|
|
if drawing.pending.mode == 'freehand' then
|
2022-07-13 05:58:46 +01:00
|
|
|
drawing.pending.vertices = {Drawing.find_or_insert_point(drawing.points, drawing.pending.points[1].x, drawing.pending.points[1].y, State.width)}
|
2022-05-18 23:29:18 +01:00
|
|
|
elseif drawing.pending.mode == 'line' or drawing.pending.mode == 'manhattan' then
|
|
|
|
if drawing.pending.vertices == nil then
|
|
|
|
drawing.pending.vertices = {drawing.pending.p1}
|
|
|
|
end
|
2022-05-26 01:07:19 +01:00
|
|
|
elseif drawing.pending.mode == 'polygon' then
|
|
|
|
while #drawing.pending.vertices > 2 do
|
|
|
|
table.remove(drawing.pending.vertices)
|
|
|
|
end
|
2022-06-15 16:53:34 +01:00
|
|
|
elseif drawing.pending.mode == 'rectangle' then
|
|
|
|
-- reuse existing (1-2) vertices
|
|
|
|
elseif drawing.pending.mode == 'circle' or drawing.pending.mode == 'arc' then
|
|
|
|
drawing.pending.vertices = {drawing.pending.center}
|
2022-05-18 23:29:18 +01:00
|
|
|
end
|
|
|
|
drawing.pending.mode = 'square'
|
2022-07-13 00:21:03 +01:00
|
|
|
elseif App.mouse_down(1) and chord == 'p' and State.current_drawing_mode == 'polygon' then
|
2022-07-21 01:06:16 +01:00
|
|
|
local _,drawing,line_cache = Drawing.current_drawing(State)
|
|
|
|
local mx,my = Drawing.coord(App.mouse_x()-State.left, State.width), Drawing.coord(App.mouse_y()-line_cache.starty, State.width)
|
2022-07-13 05:58:46 +01:00
|
|
|
local j = Drawing.find_or_insert_point(drawing.points, mx,my, State.width)
|
2022-05-18 06:36:10 +01:00
|
|
|
table.insert(drawing.pending.vertices, j)
|
2022-07-13 00:21:03 +01:00
|
|
|
elseif App.mouse_down(1) and chord == 'p' and (State.current_drawing_mode == 'rectangle' or State.current_drawing_mode == 'square') then
|
2022-07-21 01:06:16 +01:00
|
|
|
local _,drawing,line_cache = Drawing.current_drawing(State)
|
|
|
|
local mx,my = Drawing.coord(App.mouse_x()-State.left, State.width), Drawing.coord(App.mouse_y()-line_cache.starty, State.width)
|
2022-07-13 05:58:46 +01:00
|
|
|
local j = Drawing.find_or_insert_point(drawing.points, mx,my, State.width)
|
2022-06-14 22:59:09 +01:00
|
|
|
while #drawing.pending.vertices >= 2 do
|
|
|
|
table.remove(drawing.pending.vertices)
|
|
|
|
end
|
|
|
|
table.insert(drawing.pending.vertices, j)
|
2022-06-14 17:05:02 +01:00
|
|
|
elseif chord == 'C-o' and not App.mouse_down(1) then
|
2022-07-13 00:21:03 +01:00
|
|
|
State.current_drawing_mode = 'circle'
|
|
|
|
elseif App.mouse_down(1) and chord == 'a' and State.current_drawing_mode == 'circle' then
|
2022-07-21 01:06:16 +01:00
|
|
|
local _,drawing,line_cache = Drawing.current_drawing(State)
|
2022-05-18 06:36:10 +01:00
|
|
|
drawing.pending.mode = 'arc'
|
2022-07-21 01:06:16 +01:00
|
|
|
local mx,my = Drawing.coord(App.mouse_x()-State.left, State.width), Drawing.coord(App.mouse_y()-line_cache.starty, State.width)
|
2022-05-18 06:36:10 +01:00
|
|
|
local center = drawing.points[drawing.pending.center]
|
2022-07-30 15:13:45 +01:00
|
|
|
drawing.pending.radius = round(geom.dist(center.x,center.y, mx,my))
|
2022-05-18 06:36:10 +01:00
|
|
|
drawing.pending.start_angle = geom.angle(center.x,center.y, mx,my)
|
2022-06-14 17:05:02 +01:00
|
|
|
elseif App.mouse_down(1) and chord == 'o' then
|
2022-07-13 00:21:03 +01:00
|
|
|
State.current_drawing_mode = 'circle'
|
2022-07-13 01:27:00 +01:00
|
|
|
local _,drawing = Drawing.current_drawing(State)
|
2022-05-18 06:36:10 +01:00
|
|
|
if drawing.pending.mode == 'freehand' then
|
2022-07-13 05:58:46 +01:00
|
|
|
drawing.pending.center = Drawing.find_or_insert_point(drawing.points, drawing.pending.points[1].x, drawing.pending.points[1].y, State.width)
|
2022-05-18 06:36:10 +01:00
|
|
|
elseif drawing.pending.mode == 'line' or drawing.pending.mode == 'manhattan' then
|
|
|
|
drawing.pending.center = drawing.pending.p1
|
2022-05-26 01:07:19 +01:00
|
|
|
elseif drawing.pending.mode == 'polygon' or drawing.pending.mode == 'rectangle' or drawing.pending.mode == 'square' then
|
2022-05-18 06:36:10 +01:00
|
|
|
drawing.pending.center = drawing.pending.vertices[1]
|
|
|
|
end
|
|
|
|
drawing.pending.mode = 'circle'
|
2022-06-14 17:05:02 +01:00
|
|
|
elseif chord == 'C-u' and not App.mouse_down(1) then
|
2022-07-21 01:06:16 +01:00
|
|
|
local drawing_index,drawing,line_cache,i,p = Drawing.select_point_at_mouse(State)
|
2022-05-18 06:36:10 +01:00
|
|
|
if drawing then
|
2022-07-13 00:21:03 +01:00
|
|
|
if State.previous_drawing_mode == nil then
|
|
|
|
State.previous_drawing_mode = State.current_drawing_mode
|
2022-05-30 23:34:53 +01:00
|
|
|
end
|
2022-07-13 00:21:03 +01:00
|
|
|
State.current_drawing_mode = 'move'
|
|
|
|
drawing.pending = {mode=State.current_drawing_mode, target_point=p, target_point_index=i}
|
|
|
|
State.lines.current_drawing_index = drawing_index
|
|
|
|
State.lines.current_drawing = drawing
|
2022-05-18 06:36:10 +01:00
|
|
|
end
|
2022-06-14 17:05:02 +01:00
|
|
|
elseif chord == 'C-n' and not App.mouse_down(1) then
|
2022-07-21 01:06:16 +01:00
|
|
|
local drawing_index,drawing,line_cache,point_index,p = Drawing.select_point_at_mouse(State)
|
2022-05-21 22:03:06 +01:00
|
|
|
if drawing then
|
2022-07-13 00:21:03 +01:00
|
|
|
if State.previous_drawing_mode == nil then
|
2022-05-30 23:34:53 +01:00
|
|
|
-- don't clobber
|
2022-07-13 00:21:03 +01:00
|
|
|
State.previous_drawing_mode = State.current_drawing_mode
|
2022-05-30 23:34:53 +01:00
|
|
|
end
|
2022-07-13 00:21:03 +01:00
|
|
|
State.current_drawing_mode = 'name'
|
2022-05-21 22:03:06 +01:00
|
|
|
p.name = ''
|
2022-07-13 00:21:03 +01:00
|
|
|
drawing.pending = {mode=State.current_drawing_mode, target_point=point_index}
|
|
|
|
State.lines.current_drawing_index = drawing_index
|
|
|
|
State.lines.current_drawing = drawing
|
2022-05-21 22:03:06 +01:00
|
|
|
end
|
2022-06-14 17:05:02 +01:00
|
|
|
elseif chord == 'C-d' and not App.mouse_down(1) then
|
2022-07-21 01:06:16 +01:00
|
|
|
local _,drawing,_,i,p = Drawing.select_point_at_mouse(State)
|
2022-05-18 06:36:10 +01:00
|
|
|
if drawing then
|
|
|
|
for _,shape in ipairs(drawing.shapes) do
|
|
|
|
if Drawing.contains_point(shape, i) then
|
|
|
|
if shape.mode == 'polygon' then
|
|
|
|
local idx = table.find(shape.vertices, i)
|
|
|
|
assert(idx)
|
|
|
|
table.remove(shape.vertices, idx)
|
|
|
|
if #shape.vertices < 3 then
|
|
|
|
shape.mode = 'deleted'
|
|
|
|
end
|
|
|
|
else
|
|
|
|
shape.mode = 'deleted'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
drawing.points[i].deleted = true
|
|
|
|
end
|
2022-07-21 01:06:16 +01:00
|
|
|
local drawing,_,_,shape = Drawing.select_shape_at_mouse(State)
|
2022-05-18 06:36:10 +01:00
|
|
|
if drawing then
|
|
|
|
shape.mode = 'deleted'
|
|
|
|
end
|
2022-06-14 17:05:02 +01:00
|
|
|
elseif chord == 'C-h' and not App.mouse_down(1) then
|
2022-07-13 01:27:00 +01:00
|
|
|
local drawing = Drawing.select_drawing_at_mouse(State)
|
2022-05-18 06:36:10 +01:00
|
|
|
if drawing then
|
|
|
|
drawing.show_help = true
|
|
|
|
end
|
2022-07-05 19:04:39 +01:00
|
|
|
elseif chord == 'escape' and App.mouse_down(1) then
|
2022-07-13 01:27:00 +01:00
|
|
|
local _,drawing = Drawing.current_drawing(State)
|
2022-07-05 19:04:39 +01:00
|
|
|
drawing.pending = {}
|
2022-05-18 06:36:10 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-05-18 23:29:18 +01:00
|
|
|
function Drawing.complete_rectangle(firstx,firsty, secondx,secondy, x,y)
|
|
|
|
if firstx == secondx then
|
2022-05-21 16:20:55 +01:00
|
|
|
return x,secondy, x,firsty
|
2022-05-18 23:29:18 +01:00
|
|
|
end
|
|
|
|
if firsty == secondy then
|
2022-05-21 16:20:55 +01:00
|
|
|
return secondx,y, firstx,y
|
2022-05-18 23:29:18 +01:00
|
|
|
end
|
|
|
|
local first_slope = (secondy-firsty)/(secondx-firstx)
|
|
|
|
-- slope of second edge:
|
|
|
|
-- -1/first_slope
|
|
|
|
-- equation of line containing the second edge:
|
|
|
|
-- y-secondy = -1/first_slope*(x-secondx)
|
|
|
|
-- => 1/first_slope*x + y + (- secondy - secondx/first_slope) = 0
|
|
|
|
-- now we want to find the point on this line that's closest to the mouse pointer.
|
|
|
|
-- https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Line_defined_by_an_equation
|
|
|
|
local a = 1/first_slope
|
|
|
|
local c = -secondy - secondx/first_slope
|
2022-07-30 05:54:06 +01:00
|
|
|
local thirdx = round(((x-a*y) - a*c) / (a*a + 1))
|
|
|
|
local thirdy = round((a*(-x + a*y) - c) / (a*a + 1))
|
2022-05-18 23:29:18 +01:00
|
|
|
-- slope of third edge = first_slope
|
|
|
|
-- equation of line containing third edge:
|
|
|
|
-- y - thirdy = first_slope*(x-thirdx)
|
|
|
|
-- => -first_slope*x + y + (-thirdy + thirdx*first_slope) = 0
|
|
|
|
-- now we want to find the point on this line that's closest to the first point
|
|
|
|
local a = -first_slope
|
|
|
|
local c = -thirdy + thirdx*first_slope
|
2022-07-30 05:54:06 +01:00
|
|
|
local fourthx = round(((firstx-a*firsty) - a*c) / (a*a + 1))
|
|
|
|
local fourthy = round((a*(-firstx + a*firsty) - c) / (a*a + 1))
|
2022-05-18 23:29:18 +01:00
|
|
|
return thirdx,thirdy, fourthx,fourthy
|
|
|
|
end
|
|
|
|
|
|
|
|
function Drawing.complete_square(firstx,firsty, secondx,secondy, x,y)
|
|
|
|
-- use x,y only to decide which side of the first edge to complete the square on
|
|
|
|
local deltax = secondx-firstx
|
|
|
|
local deltay = secondy-firsty
|
|
|
|
local thirdx = secondx+deltay
|
|
|
|
local thirdy = secondy-deltax
|
|
|
|
if not geom.same_side(firstx,firsty, secondx,secondy, thirdx,thirdy, x,y) then
|
|
|
|
deltax = -deltax
|
|
|
|
deltay = -deltay
|
|
|
|
thirdx = secondx+deltay
|
|
|
|
thirdy = secondy-deltax
|
|
|
|
end
|
|
|
|
local fourthx = firstx+deltay
|
|
|
|
local fourthy = firsty-deltax
|
|
|
|
return thirdx,thirdy, fourthx,fourthy
|
|
|
|
end
|
|
|
|
|
2022-07-13 01:27:00 +01:00
|
|
|
function Drawing.current_drawing(State)
|
2022-06-12 16:26:37 +01:00
|
|
|
local x, y = App.mouse_x(), App.mouse_y()
|
2022-07-13 01:27:00 +01:00
|
|
|
for drawing_index,drawing in ipairs(State.lines) do
|
2022-05-18 05:18:17 +01:00
|
|
|
if drawing.mode == 'drawing' then
|
2022-07-21 01:06:16 +01:00
|
|
|
local line_cache = State.line_cache[drawing_index]
|
|
|
|
if Drawing.in_drawing(drawing, line_cache, x,y, State.left,State.right) then
|
|
|
|
return drawing_index,drawing,line_cache
|
2022-05-18 05:18:17 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
2022-07-13 01:27:00 +01:00
|
|
|
function Drawing.select_shape_at_mouse(State)
|
2022-07-21 01:06:16 +01:00
|
|
|
for drawing_index,drawing in ipairs(State.lines) do
|
2022-05-18 05:18:17 +01:00
|
|
|
if drawing.mode == 'drawing' then
|
2022-06-12 16:26:37 +01:00
|
|
|
local x, y = App.mouse_x(), App.mouse_y()
|
2022-07-21 01:06:16 +01:00
|
|
|
local line_cache = State.line_cache[drawing_index]
|
|
|
|
if Drawing.in_drawing(drawing, line_cache, x,y, State.left,State.right) then
|
|
|
|
local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width)
|
2022-05-18 05:18:17 +01:00
|
|
|
for i,shape in ipairs(drawing.shapes) do
|
|
|
|
assert(shape)
|
|
|
|
if geom.on_shape(mx,my, drawing, shape) then
|
2022-07-21 01:06:16 +01:00
|
|
|
return drawing,line_cache,i,shape
|
2022-05-18 05:18:17 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-07-13 01:27:00 +01:00
|
|
|
function Drawing.select_point_at_mouse(State)
|
|
|
|
for drawing_index,drawing in ipairs(State.lines) do
|
2022-05-18 05:18:17 +01:00
|
|
|
if drawing.mode == 'drawing' then
|
2022-06-12 16:26:37 +01:00
|
|
|
local x, y = App.mouse_x(), App.mouse_y()
|
2022-07-21 01:06:16 +01:00
|
|
|
local line_cache = State.line_cache[drawing_index]
|
|
|
|
if Drawing.in_drawing(drawing, line_cache, x,y, State.left,State.right) then
|
|
|
|
local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width)
|
2022-05-18 05:18:17 +01:00
|
|
|
for i,point in ipairs(drawing.points) do
|
|
|
|
assert(point)
|
2022-07-13 05:58:46 +01:00
|
|
|
if Drawing.near(point, mx,my, State.width) then
|
2022-07-21 01:06:16 +01:00
|
|
|
return drawing_index,drawing,line_cache,i,point
|
2022-05-18 05:18:17 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-07-13 01:27:00 +01:00
|
|
|
function Drawing.select_drawing_at_mouse(State)
|
2022-07-21 01:06:16 +01:00
|
|
|
for drawing_index,drawing in ipairs(State.lines) do
|
2022-05-18 05:18:17 +01:00
|
|
|
if drawing.mode == 'drawing' then
|
2022-06-12 16:26:37 +01:00
|
|
|
local x, y = App.mouse_x(), App.mouse_y()
|
2022-07-21 01:06:16 +01:00
|
|
|
local line_cache = State.line_cache[drawing_index]
|
|
|
|
if Drawing.in_drawing(drawing, line_cache, x,y, State.left,State.right) then
|
2022-05-18 05:18:17 +01:00
|
|
|
return drawing
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function Drawing.contains_point(shape, p)
|
|
|
|
if shape.mode == 'freehand' then
|
|
|
|
-- not supported
|
|
|
|
elseif shape.mode == 'line' or shape.mode == 'manhattan' then
|
|
|
|
return shape.p1 == p or shape.p2 == p
|
2022-05-31 00:22:35 +01:00
|
|
|
elseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' then
|
2022-05-18 05:18:17 +01:00
|
|
|
return table.find(shape.vertices, p)
|
|
|
|
elseif shape.mode == 'circle' then
|
|
|
|
return shape.center == p
|
|
|
|
elseif shape.mode == 'arc' then
|
|
|
|
return shape.center == p
|
|
|
|
-- ugh, how to support angles
|
|
|
|
elseif shape.mode == 'deleted' then
|
|
|
|
-- already done
|
|
|
|
else
|
|
|
|
print(shape.mode)
|
|
|
|
assert(false)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-07-05 19:32:45 +01:00
|
|
|
function Drawing.smoothen(shape)
|
|
|
|
assert(shape.mode == 'freehand')
|
|
|
|
for _=1,7 do
|
|
|
|
for i=2,#shape.points-1 do
|
|
|
|
local a = shape.points[i-1]
|
|
|
|
local b = shape.points[i]
|
|
|
|
local c = shape.points[i+1]
|
2022-07-30 05:54:06 +01:00
|
|
|
b.x = round((a.x + b.x + c.x)/3)
|
|
|
|
b.y = round((a.y + b.y + c.y)/3)
|
2022-07-05 19:32:45 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-07-30 05:54:06 +01:00
|
|
|
function round(num)
|
|
|
|
return math.floor(num+.5)
|
|
|
|
end
|
|
|
|
|
2022-07-13 05:58:46 +01:00
|
|
|
function Drawing.find_or_insert_point(points, x,y, width)
|
2022-07-13 05:29:27 +01:00
|
|
|
-- check if UI would snap the two points together
|
2022-05-18 05:18:17 +01:00
|
|
|
for i,point in ipairs(points) do
|
2022-07-13 05:58:46 +01:00
|
|
|
if Drawing.near(point, x,y, width) then
|
2022-05-18 05:18:17 +01:00
|
|
|
return i
|
|
|
|
end
|
|
|
|
end
|
|
|
|
table.insert(points, {x=x, y=y})
|
|
|
|
return #points
|
|
|
|
end
|
|
|
|
|
2022-07-13 05:58:46 +01:00
|
|
|
function Drawing.near(point, x,y, width)
|
|
|
|
local px,py = Drawing.pixels(x, width),Drawing.pixels(y, width)
|
|
|
|
local cx,cy = Drawing.pixels(point.x, width), Drawing.pixels(point.y, width)
|
2022-07-13 05:38:29 +01:00
|
|
|
return (cx-px)*(cx-px) + (cy-py)*(cy-py) < Same_point_distance*Same_point_distance
|
2022-05-18 05:18:17 +01:00
|
|
|
end
|
|
|
|
|
2022-07-13 05:58:46 +01:00
|
|
|
function Drawing.pixels(n, width) -- parts to pixels
|
|
|
|
return math.floor(n*width/256)
|
2022-05-18 05:18:17 +01:00
|
|
|
end
|
2022-07-13 06:05:36 +01:00
|
|
|
function Drawing.coord(n, width) -- pixels to parts
|
|
|
|
return math.floor(n*256/width)
|
2022-05-18 03:41:42 +01:00
|
|
|
end
|
|
|
|
|
2022-05-18 06:36:10 +01:00
|
|
|
function table.find(h, x)
|
|
|
|
for k,v in pairs(h) do
|
|
|
|
if v == x then
|
|
|
|
return k
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|