commit 5bae7b7947be0058653ceeee9c14a593c87b0c09
parent 6d377721c041130d4124982a05a992a3b4c405aa
Author: Nihal Jere <nihal@nihaljere.xyz>
Date: Wed, 21 Dec 2022 18:07:52 -0600
badly animated tie
Diffstat:
M | main.c | | | 52 | ++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | score.sp | | | 2 | +- |
M | smallpond.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