smallpond

Unnamed repository; edit this file 'description' to name the repository.
git clone git://git.nihaljere.xyz/smallpond
Log | Files | Refs | README | LICENSE

commit 5bae7b7947be0058653ceeee9c14a593c87b0c09
parent 6d377721c041130d4124982a05a992a3b4c405aa
Author: Nihal Jere <nihal@nihaljere.xyz>
Date:   Wed, 21 Dec 2022 18:07:52 -0600

badly animated tie

Diffstat:
Mmain.c | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Mscore.sp | 2+-
Msmallpond.lua | 37++++++++++++++++++++++++++++++++++---
3 files changed, 87 insertions(+), 4 deletions(-)

diff --git a/main.c b/main.c @@ -27,6 +27,56 @@ FT_Face face; cairo_font_face_t *cface; cairo_surface_t *surface; +// return control points for cubic bezier curve corresponding to +// bezier curve that t of the way through the bezier curve produced +// by the control points. + +// see https://en.wikipedia.org/wiki/B%C3%A9zier_curve#/media/File:B%C3%A9zier_3_big.svg +// for explanation of variable names +int +split_cubic(double t, double x1, double y1, double x2, double y2, double x, double y, double *q0x, double *q0y, double *r0x, double *r0y, double *bx, double *by) +{ + *q0x = x1 * t; + *q0y = y1 * t; + + double q1x = (x2 - x1) * t + x1; + double q1y = (y2 - y1) * t + y1; + + double q2x = (x - x2) * t + x2; + double q2y = (y - y2) * t + y2; + + double r1x = (q2x - q1x) * t + q1x; + double r1y = (q2y - q1y) * t + q1y; + + *r0x = (q1x - *q0x) * t + *q0x; + *r0y = (q1y - *q0y) * t + *q0y; + + *bx = (r1x - *r0x) * t + *r0x; + *by = (r1y - *r0y) * t + *r0y; +} + +int +draw_curve(lua_State *L) +{ + double x0 = lua_tonumber(L, -6); + double y0 = lua_tonumber(L, -5); + double x1 = lua_tonumber(L, -4); + double y1 = lua_tonumber(L, -3); + double x2 = lua_tonumber(L, -2); + double y2 = lua_tonumber(L, -1); + + cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); + + cairo_curve_to(cr, x0, y0, x1, y1, x2, y2); + cairo_line_to(cr, x2, y2 + 1); + cairo_curve_to(cr, x1, y1 + 5, x0, y0 + 1, x0, y0 + 1); + cairo_line_to(cr, x0, y0); + cairo_set_line_width(cr, 1); + cairo_fill(cr); + + return 0; +} + int draw_circle(lua_State *L) { @@ -178,6 +228,8 @@ main(int argc, char *argv[]) lua_setglobal(L, "Q"); // load drawing primitives + lua_pushcfunction(L, draw_curve); + lua_setglobal(L, "draw_curve"); lua_pushcfunction(L, draw_glyph); lua_setglobal(L, "draw_glyph"); lua_pushcfunction(L, draw_circle); diff --git a/score.sp b/score.sp @@ -171,7 +171,7 @@ 31.27a^16[ \staff high \clef treble - 31.82cs,v16 32.0fsv16 32.29a'v16 32.550csv8] + 31.82cs,v16 32.0fsv16 32.29a'v16 32.550cs~v8] 32.82csv16[ 32.85av16 33.08fs,v16 33.40csv16 \staff low \clef bass diff --git a/smallpond.lua b/smallpond.lua @@ -119,7 +119,7 @@ local commands = { end local parsenote = function(text, start) -- TODO: should we be more strict about accidentals and stem orientations on rests? - local s, e, time, note, acc, shift = string.find(text, "^(%d*%.?%d*)([abcdefgs])([fns]?)([,']*)", start) + local s, e, time, note, acc, shift, tie = string.find(text, "^(%d*%.?%d*)([abcdefgs])([fns]?)([,']*)(~?)", start) if note then local out if note == 's' then @@ -131,6 +131,8 @@ local commands = { local _, down = string.gsub(shift, ',', '') local _, up = string.gsub(shift, "'", '') out.shift = up - down + + if #tie > 0 then out.tie = true end return start + e - s + 1, out end @@ -299,6 +301,8 @@ local inbeam = false local beam local beams = {} local beamednotes +local unterminated_ties = {} +local ties = {} -- first-order placement local dispatch1 = { newnotegroup = function(data) @@ -333,6 +337,16 @@ local dispatch1 = { if note.time and not mintime then mintime = note.time end if maxtime and note.time and note.time < maxtime then maxtime = note.time end + + if unterminated_ties[curname] and unterminated_ties[curname].y == head.y then + table.insert(ties, {staff=curname, start=unterminated_ties[curname], stop=head}) + unterminated_ties[curname] = nil + end + + if note.tie then + assert(unterminated_ties[curname] == nil) + unterminated_ties[curname] = head + end end @@ -538,10 +552,12 @@ local staff3ify = function(timing, el, staff) if not lowheight then lowheight = ry end if not highheight then highheight = ry end if head.flip then - table.insert(staff3[staff], {kind="glyph", size=glyphsize, glyph=glyph, x=preoffset + rx, y=ry, time={start=head.time}}) + head.glyph = {kind="glyph", width=w, size=glyphsize, glyph=glyph, x=preoffset + rx, y=ry, time={start=head.time}} else - table.insert(staff3[staff], {kind="glyph", size=glyphsize, glyph=glyph, x=preoffset + altoffset + rx, y=ry, time={start=head.time}}) + head.glyph = {kind="glyph", width=w, size=glyphsize, glyph=glyph, x=preoffset + altoffset + rx, y=ry, time={start=head.time}} end + table.insert(staff3[staff], head.glyph) + if el.dot then xdiff = xdiff + 5 table.insert(staff3[staff], {kind="circle", r=1.5, x=preoffset + altoffset + rx + w + 5, y=ry, time={start=head.time}}) @@ -822,6 +838,11 @@ for i, staff in pairs(stafforder) do yoff = yoff + extent.ymax - extent.ymin end +for _, tie in pairs(ties) do + local yoff = extents[tie.staff].yoff - extents[tie.staff].ymin + table.insert(extra3, {kind="curve", x0=tie.start.glyph.x + tie.start.glyph.width + 5, y0=tie.start.glyph.y + yoff, x2=tie.stop.glyph.x, y2=tie.stop.glyph.y + yoff, time={start=tie.start.glyph.time.start, stop=tie.stop.glyph.time.start}}) +end + -- draw beam (and adjust stems) after all previous notes already have set values for _, notes in ipairs(beams) do local beamheight, beamspace @@ -986,6 +1007,16 @@ function drawframe(time) local endy = math.min(y1 + delta*(y2 - y1), y2) draw_line(1, toff + item.x, y1, toff + item.x, endy) + elseif item.kind == "curve" then + if item.time.start > time then goto continue end + local delta + if item.time.stop < time then + delta = 1 + else + delta = (time - item.time.start) / (item.time.stop - item.time.start) + end + local endx = item.x0 + delta*(item.x2 - item.x0) + draw_curve(toff + item.x0, item.y0, toff + (item.x0 + endx) / 2, (item.y0 + item.y2) / 2 + 20, toff + endx, item.y2) elseif item.kind == "beamseg" then if item.time.start > time then goto continue end local delta