local player = require'cplayer' local glue = require'glue' local path = require'path2d' local matrix = require'affine2d' local b2_to_b3 = require'path2d_bezier2'.to_bezier3 local function draw_function(cr) local cpx, cpy local function write(s, ...) if s == 'move' then cr:move_to(...) cpx, cpy = ... elseif s == 'close' then cr:close_path() elseif s == 'line' then cr:line_to(...) cpx, cpy = ... elseif s == 'curve' then cr:curve_to(...) cpx, cpy = select(5, ...) elseif s == 'quad_curve' then cr:curve_to(select(3, b2_to_b3(cpx, cpy, ...))) cpx, cpy = select(3, ...) elseif s == 'text' then local x1, y1, font, text = ... cr:font_face(font.family or 'Arial') cr:font_size(font.size or 12) cr:move_to(x1, y1) cr:text_path(tostring(text)) cpx, cpy = nil end end local function draw(path_, mt) cr:new_sub_path() path.decompose(write, path_, mt) end return draw end local subpaths, abs_rel, inspect function player:on_render(cr) local p = { 'move', 900, 110, --lines and control commands 'rel_line', 20, 100, 'rel_hline', 100, 'rel_vline', -100, 'close', 'rel_line', 50, 50, --quad curves 'move', 100, 160, 'rel_quad_curve', 20, -100, 40, 0, 'rel_symm_quad_curve', 40, 0, 'rel_move', 50, 0, 'rel_smooth_quad_curve', 100, 20, 0, --smooth without a tangent 'rel_move', 50, 0, 'rel_quad_curve', 20, -100, 20, 0, 'rel_smooth_quad_curve', 50, 40, 0, --smooth a quad curve 'rel_move', 50, 0, 'rel_curve', 0, -50, 40, -50, 40, 0, 'rel_smooth_quad_curve', 50, 40, 0, --smooth a cubic curve 'rel_move', 50, 0, 'rel_arc_3p', 0, -40, 50, 0, 'rel_smooth_quad_curve', 50, 40, 0, --smooth an arc 'rel_move', 50, -50, 'rel_line', 0, 50, 'rel_smooth_quad_curve', 50, 40, 0, --smooth a line 'rel_move', 50, 0, 'rel_quad_curve_3p', 20, -50, 40, 0, --3p --cubic curves 'move', 100, 300, 'rel_curve', 0, -50, 40, -50, 40, 0, 'rel_symm_curve', 40, 50, 40, 0, 'rel_move', 50, 0, 'rel_smooth_curve', 100, 20, 50, 20, 0, --smooth without a tangent 'rel_move', 50, 0, 'rel_quad_curve', 20, -100, 20, 0, 'rel_smooth_curve', 50, 40, 50, 40, 0, --smooth a quad curve 'rel_move', 50, 0, 'rel_curve', 0, -50, 40, -50, 40, 0, 'rel_smooth_curve', 50, 40, 50, 40, 0, --smooth a cubic curve 'rel_move', 50, 0, 'rel_arc_3p', 0, -40, 50, 0, 'rel_smooth_curve', 50, 40, 50, 40, 0, --smooth an arc 'rel_move', 50, -50, 'rel_line', 0, 50, 'rel_smooth_curve', 50, 40, 50, 40, 0, --smooth a line --arcs 'move', 100, 450, 'rel_line_arc', 0, 0, 50, -90, 180, 'rel_move', 100, -50, 'rel_arc', 0, 0, 50, -90, 180, 'rel_move', 100, -100, 'rel_svgarc', -50, -20, -30, 0, 1, 30, 40, 'rel_svgarc', -50, -20, -30, 1, 0, 30, 40, 'rel_svgarc', 10, 0, 0, 0, 0, 50, 0, --invalid parametrization (zero radius) 'rel_svgarc', 10, 10, 10, 10, 0, 0, 0, --invalid parametrization (endpoints coincide) 'rel_move', 50, -50, 'rel_arc_3p', 40, -40, 80, 0, 'rel_arc_3p', 40, 0, 40, 0, --invalid parametrization (endpoints are collinear) 'rel_arc_3p', 0, 0, 0, 0, --invalid parametrization (endpoints coincide) 'rel_move', 70, 0, 'rel_line_elliptic_arc', 0, 0, 70, 30, 0, -270, -30, 'close', --closed shapes 'rect', 100+60, 650, -50, -100, 'round_rect', 100+120, 650, -50, -100, -10, 'elliptic_rect', 100+180, 650, -50, -100, -100, -10, 'elliptic_rect', 100+240, 650, -50, -100, -10, -100, 'circle', 100+300, 600, -50, 'ellipse', 100+390, 600, -30, -50, 30, 'move', 100+480, 600, 'rel_circle_3p', 50, 0, 0, 50, -50, 0, 'superformula', 100+580, 600, 50, 300, 30, 1, 1, 3, 1, 1, 1, 'move', 100+700, 600, 'rel_star', 0, 0, 0, -50, 30, 8, 'move', 100+800, 600, 'rel_star_2p', 0, 0, 0, -50, 20, 15, 5, 'move', 100+900, 600, 'rel_rpoly', 0, 0, 20, -30, 5, 'move', 700, 350, 'rel_text', 0, 0, {size=70}, 'mittens', } local draw = draw_function(cr) local mt = matrix():translate(150, -50) subpaths = self:mbutton{x = 10, y = 10, w = 190, h = 24, id = 'subpaths', values = {'mixed', 'open', 'closed'}, selected = subpaths or 'mixed'} abs_rel = self:mbutton{x = 10, y = 40, w = 190, h = 24, id = 'abs_rel', values = {'mixed', 'abs', 'rel'}, selected = abs_rel or 'mixed'} inspect = self:button{x = 10, y = 70, w = 190, h = 24, id = 'inspect'} if subpaths == 'closed' then --close all non-empty subpaths for i,s in path.subpaths(p) do if not path.subpath_is_empty(p,i) then path.close_subpath(p,i) end end elseif subpaths == 'open' then --open all non-empty subpaths for i,s in path.subpaths(p) do if not path.subpath_is_empty(p,i) then path.open_subpath(p,i) end end end --convert to abs. or rel. if abs_rel == 'abs' then p = path.to_abs(p) elseif abs_rel == 'rel' then p = path.to_rel(p) end --hit testing local d,x,y,i,t = path.hit(self.mousex, self.mousey, p, mt) local hot = d < 20 local selected = hot and self.lbutton if hot then self:circle(x,y,5) local cpx, cpy, spx, spy, tkind = path.state_at(p, i) self:label{x = self.mousex + 10, y = self.mousey + 22, text = string.format('%d %s\nt: %g\ncp: %s, %s\nsp: %s, %s\ntip: %s', i or 0, p[i] or 'n/a', t or 0, cpx and string.format('%4.2f', cpx) or 'nil', cpx and string.format('%4.2f', cpy) or 'nil', spy and string.format('%4.2f', spx) or 'nil', spy and string.format('%4.2f', spy) or 'nil', tkind ) } end draw(p, mt) self:stroke('normal_fg') --bounding box of the entire path if true then local x,y,w,h = path.bounding_box(p, mt) self:rect(x,y,w,h, nil, 'normal_fg', 0.3) end --bounding boxes of subpaths if true then for i,s in path.subpaths(p) do local j = path.next_subpath(p, i) j = j and j-1 local subp = path.extract_subpath(p, i, j) local x,y,w,h = path.bounding_box(subp, mt) self:rect(x,y,w,h, nil, 'normal_fg', 0.3) end end --inspecting --local editor = require --self:editor if hot and not selected then local p = path.extract_subpath(p, i, i + path.argc[p[i]]) draw(p, mt) self:stroke('normal_fg', 2) end --inspecting if inspect then path.inspect(p) end end player:play()