|
|
describe("Layout", function() { |
|
|
var abcBarLinesTreble = "X:1\n%%barlabelfont Times-Bold 18 box\n%%setbarnb 42\n%%barnumbers 1\nM: 4/4\nL: 1/16\nK:D\nz8 |\n z8 |\n" |
|
|
|
|
|
var expectedBarLinesTreble = [{x: 20, y: 88}] |
|
|
|
|
|
var abcBarLinesBass = "X:1\n%%barlabelfont Times-Bold 18 box\n%%setbarnb 42\n%%barnumbers 1\nM: 4/4\nL: 1/16\nK:D clef=bass\nz8 |\n z8 |\n" |
|
|
|
|
|
var expectedBarLinesBass = [{x: 20, y: 84}] |
|
|
|
|
|
var abcMinSpacing = "X:1\nL:1/8\nM:4/4\ncdef cdef|\ncdef cdef|cdef cdef|\ncdef cdef|cdef cdef|cdef cdef|\n"; |
|
|
|
|
|
var expectedMinSpacing0 = [ |
|
|
[71,96,122,147,172,198,223,249,274], |
|
|
[49,62,75,88,101,114,127,141,156,167,180,193,207,220,233,246,259,275], |
|
|
[49,60,71,81,92,103,114,125,141,152,162,173,184,195,206,216,227,243,254,265,276,286,297,308,319,330,345] |
|
|
]; |
|
|
|
|
|
var expectedMinSpacing10 = [ |
|
|
[81,105,129,153,177,202,226,250,276], |
|
|
[59,80,101,121,142,163,184,205,231,252,272,293,314,335,356,376,397,423], |
|
|
[59,80,101,121,142,163,184,205,231,252,272,293,314,335,356,376,397,423,444,465,486,506,527,548,569,590,615] |
|
|
]; |
|
|
|
|
|
var abcCollidingNotes = "X:1\n" + |
|
|
"L:1/8\n" + |
|
|
"M:4/4\n" + |
|
|
"%%score (S A)\n" + |
|
|
"V:S clef=treble middle=B stem=up\n" + |
|
|
"V:A clef=treble middle=B stem=down\n" + |
|
|
"K:C\n" + |
|
|
"[V:S]G4 zA G2 | GG GG GG GG | G/G/G/G/ [GB]/G/[cG]/G/ G/G/G/G/ G/G/G/G/ | C6 ||\n" + |
|
|
"[V:A]F2 F2 F2 F2 | FF EF FE FC | F/C/D/A,/ F/F/F/F/ [DF]/F/[FD]/F/ F/F/F/F/ | _B,6 ||"; |
|
|
|
|
|
var expectedCollidingNotes = [[{"w":24.051,"dx":5},{"w":11.795,"dx":0},{"w":10.37,"dx":0},{"w":7.534,"dx":0},{"w":15.902000000000001,"dx":9.21},{"w":9.81,"dx":0},{"w":1,"dx":0},{"w":9.81,"dx":0},{"w":9.81,"dx":0},{"w":9.81,"dx":0},{"w":9.81,"dx":0},{"w":9.81,"dx":0},{"w":9.81,"dx":0},{"w":9.81,"dx":0},{"w":9.81,"dx":0},{"w":1,"dx":0},{"w":9.81,"dx":0},{"w":9.81,"dx":0},{"w":9.81,"dx":0},{"w":9.81,"dx":0},{"w":9.81,"dx":0},{"w":9.81,"dx":0},{"w":9.81,"dx":0},{"w":9.81,"dx":0},{"w":9.81,"dx":0},{"w":9.81,"dx":0},{"w":9.81,"dx":0},{"w":9.81,"dx":0},{"w":9.81,"dx":0},{"w":9.81,"dx":0},{"w":9.81,"dx":0},{"w":9.81,"dx":0}, {"w":1,"dx":0},{"w":16.82,"dx":13.37}, {"w":4,"dx":0}], |
|
|
[ |
|
|
{"w":20.18,"dx":10.37},{"w":9.81,"dx":0},{"w":9.81,"dx":0},{"w":19.62,"dx":9.81},{"w":1,"dx":0}, |
|
|
{"w":19.62,"dx":9.81},{"w":19.62,"dx":9.81},{"w":9.81,"dx":0},{"w":19.62,"dx":9.81}, {"w":19.62,"dx":9.81},{"w":9.81,"dx":0},{"w":19.62,"dx":9.81},{"w":9.81,"dx":0}, {"w":1,"dx":0}, |
|
|
{"w":19.62,"dx":9.81},{"w":9.81,"dx":0},{"w":9.81,"dx":0},{"w":9.81,"dx":0}, |
|
|
{"w":19.62,"dx":9.81},{"w":19.62,"dx":9.81},{"w":19.62,"dx":9.81},{"w":19.62,"dx":9.81}, |
|
|
{"w":19.62,"dx":9.81},{"w":19.62,"dx":9.81},{"w":19.62,"dx":9.81},{"w":19.62,"dx":9.81}, |
|
|
{"w":19.62,"dx":9.81},{"w":19.62,"dx":9.81},{"w":19.62,"dx":9.81},{"w":19.62,"dx":9.81},{"w":1,"dx":0}, |
|
|
{"w":27.189999999999998,"dx":23.74},{"w":4,"dx":0} |
|
|
]]; |
|
|
|
|
|
var abcNotCollision1 = "X:1\n" + |
|
|
"M:3/8\n" + |
|
|
"L:1/8\n" + |
|
|
"K:C\n" + |
|
|
"C3 & ABc | [CF]3 & ABc |C3 & [FA]Bc |" |
|
|
|
|
|
var expectedNotCollision1 = [[{"w":24.051,"dx":5},{"w":10.926,"dx":0.5955000000000004},{"w":16.26,"dx":12.81},{"w":1,"dx":0},{"w":16.26,"dx":12.81},{"w":1,"dx":0},{"w":16.26,"dx":12.81},{"w":1,"dx":0}],[{"w":9.81,"dx":0},{"w":9.81,"dx":0},{"w":9.81,"dx":0},{"w":1,"dx":0},{"w":9.81,"dx":0},{"w":9.81,"dx":0},{"w":9.81,"dx":0},{"w":1,"dx":0},{"w":9.81,"dx":0},{"w":9.81,"dx":0},{"w":9.81,"dx":0},{"w":1,"dx":0}]] |
|
|
|
|
|
var twoStavesCollision = "X: 1\n" + |
|
|
"%%staves [(1 2) (3 4)]\n" + |
|
|
"V: 1 clef=treble\n" + |
|
|
"V: 2 clef=treble\n" + |
|
|
"V: 3 clef=bass\n" + |
|
|
"V: 4 clef=bass\n" + |
|
|
"K: C\n" + |
|
|
"[V: 1]A2 |d2 |\n" + |
|
|
"[V: 2]F2 |d2 |\n" + |
|
|
"[V: 3]A,2 |E,2 |\n" + |
|
|
"[V: 4]D,2 |D,2 |\n"; |
|
|
|
|
|
var expectedTwoStavesCollision = [ |
|
|
[ |
|
|
{"w": 24.051, "dx": 5}, {"w": 9.81, "dx": 0}, {"w": 1, "dx": 0}, {"w": 9.81, "dx": 0}, {"w": 1, "dx": 0} |
|
|
], [ |
|
|
{"w": 9.81, "dx": 0}, {"w": 1, "dx": 0}, {"w": 9.81, "dx": 0}, {"w": 1, "dx": 0} |
|
|
], [ |
|
|
{"w": 25.153, "dx": 5}, {"w": 9.81, "dx": 0}, {"w": 1, "dx": 0}, {"w": 9.81, "dx": 0}, {"w": 1, "dx": 0} |
|
|
], [ |
|
|
{"w": 9.81, "dx": 0}, {"w": 1, "dx": 0}, {"w": 19.62, "dx": 9.81}, {"w": 1, "dx": 0} |
|
|
] |
|
|
]; |
|
|
|
|
|
var abcNotCollision2 = "X:1\n" + |
|
|
"L:1/4\n" + |
|
|
"K:GMin\n" + |
|
|
"V:1 up\n" + |
|
|
"V:2 merge down\n" + |
|
|
"V:3 bass,, up\n" + |
|
|
"V:4 bass,, merge down\n" + |
|
|
"K:GMin\n" + |
|
|
"[V:1] B2 A2 |\n" + |
|
|
"[V:2] G2 ^F2 |\n" + |
|
|
"[V:3] D3 C|\n" + |
|
|
"[V:4] D, C, D,2|" |
|
|
|
|
|
var expectedNotCollision2 = [ |
|
|
[ |
|
|
{"w":24.051,"dx":5},{"w":15.5,"dx":0},{"w":10.37,"dx":0},{"w":10.37,"dx":0},{"w":1,"dx":0} |
|
|
],[ |
|
|
{"w":10.37,"dx":0},{"w":10.37,"dx":-10.25},{"w":1,"dx":0} |
|
|
],[ |
|
|
{"w":25.153,"dx":5},{"w":15.5,"dx":0},{"w":16.82,"dx":13.37},{"w":9.81,"dx":0},{"w":1,"dx":0} |
|
|
],[ |
|
|
{"w":9.81,"dx":0},{"w":9.81,"dx":0},{"w":10.37,"dx":0},{"w":1,"dx":0} |
|
|
] |
|
|
]; |
|
|
|
|
|
var abcChordLayout = '"F"c3c|"C7"c2df|1f4- & "F"xx"Bb"x"Bbm"x|"F"f3z:|2f4- & "Bb"!style=harmonic!d2 "F"!style=harmonic!c "C7"!style=harmonic!B|"F"f4 & !style=harmonic!A4||\n' |
|
|
|
|
|
var expectedChordLayout = [{"x":54,"y":32},{"x":106,"y":32},{"x":344,"y":32},{"x":533,"y":32},{"x":190,"y":32},{"x":208,"y":32},{"x":254,"y":32},{"x":405,"y":32},{"x":455,"y":32},{"x":476,"y":32}]; |
|
|
|
|
|
var abcStaccatoPlacement = "E.B .B" |
|
|
|
|
|
var expectedStaccatoPlacement = [{ x: 82, y: 64 }, { x: 112, y: 40 }] |
|
|
|
|
|
var abcRhythmPlacement = "R: reel\n" + |
|
|
"C" |
|
|
|
|
|
var expectedRhythmPlacement = [{ x: 20, y: 53 }] |
|
|
|
|
|
var lineTooWide = |
|
|
"T:The title should be centered\n" + |
|
|
"R:Left\n" + |
|
|
"C:Right\n" + |
|
|
"L:1/4\n" + |
|
|
"K:B\n" + |
|
|
"c2c2|cccc|c2c2|cccc|\n" + |
|
|
'T:Subtitle\n' + |
|
|
'%%center Here is some centered text\n' + |
|
|
"G/G/G/G/G/G/G/G/|G/G/G/G/G/G/G/G/|G/G/G/G/G/G/G/G/|G/G/G/G/G/G/G/G/|G/G/G/G/G/G/G/G/|\n" |
|
|
|
|
|
|
|
|
var expectedLineTooWide = [ |
|
|
[108,157,205,216,251,285,319,354,365,413,462,473,507,541,575,610], |
|
|
[313], |
|
|
[313], |
|
|
[108,119,130,141,152,162,173,184,200,211,222,232,243,254,265,276,287,302,313,324,335,346,357,367,378,389,405,416,427,438,448,459,470,481,492,507,518,529,540,551,562,573,583,594,610] |
|
|
] |
|
|
|
|
|
var equalSpacing = |
|
|
"T: Notes should take up the space proportional to their duration\n" + |
|
|
"L:1/4\n" + |
|
|
"Q:1/4=83\n" + |
|
|
"K:C\n" + |
|
|
"C (3DEF G/A/ | f4 | B//c//d//e// f2 (3g/f/e/ |z d z/c/ B//z// A/|\n" + |
|
|
"z d z/c/ B//z// A/|C (3DEF G/A/ | f4 | B//c//d//e// f2 (3g/f/e/ |\n" + |
|
|
" B//c//d//e// f2 (3g/f/e/ |z d z/c/ B//z// A/|C (3DEF G/A/ | f4 |\n" + |
|
|
"C4 | f4 | g3/2 a/ f3/4 e// d3/8 c/// B// A//|d3 c|\n" + |
|
|
"^C (3^D^E^F ^G/^A/ | f4 | g3/2 a/ f3/4 e// d3/8 c/// B// A//|d3 c|\n" + |
|
|
"\n" |
|
|
|
|
|
var accentSpacing = "X:1\n" + |
|
|
"L:1/4\n" + |
|
|
"%%staffwidth 100\n" + |
|
|
"K:C\n" + |
|
|
"A/ !>!A/ |]\n" + |
|
|
"w: L R|\n" + |
|
|
"A/!>!A/ |]\n" + |
|
|
"w: L R|\n" |
|
|
|
|
|
var expectedAccentSpacing = [ |
|
|
{x: 49, y: 82}, |
|
|
{x: 91, y: 82}, |
|
|
{x: 49, y: 174}, |
|
|
{x: 91, y: 174}, |
|
|
] |
|
|
|
|
|
it("line-too-wide", function() { |
|
|
var visualObj = doLayoutTest(lineTooWide, {staffwidth: 500, expandToWidest: true }, expectedLineTooWide, 'staffwidth=500'); |
|
|
var expected = ['', 313, '', '', 15, 611, ''] |
|
|
var results = [] |
|
|
|
|
|
for (var i = 0; i < visualObj[0].topText.rows.length; i++) { |
|
|
var left = visualObj[0].topText.rows[i].left |
|
|
results.push(left ? Math.round(left) : '') |
|
|
} |
|
|
chai.assert.deepStrictEqual(results, expected, "top text"); |
|
|
}) |
|
|
|
|
|
it("min-spacing", function() { |
|
|
doLayoutTest(abcMinSpacing, {staffwidth: 260 }, expectedMinSpacing0, 'minPadding=0'); |
|
|
doLayoutTest(abcMinSpacing, {staffwidth: 260, minPadding: 10 }, expectedMinSpacing10, 'minPadding=10'); |
|
|
}) |
|
|
|
|
|
it("colliding-notes1", function() { |
|
|
doCollidingNotesTest(abcCollidingNotes, expectedCollidingNotes) |
|
|
}) |
|
|
|
|
|
it("not-colliding-notes", function() { |
|
|
doCollidingNotesTest(abcNotCollision1, expectedNotCollision1) |
|
|
}) |
|
|
|
|
|
it("two-staves-collision", function() { |
|
|
doCollidingNotesTest(twoStavesCollision, expectedTwoStavesCollision) |
|
|
}) |
|
|
|
|
|
it("not-collision", function() { |
|
|
doCollidingNotesTest(abcNotCollision2, expectedNotCollision2) |
|
|
}) |
|
|
|
|
|
it("chord-layout", function() { |
|
|
doChordLayoutTest(abcChordLayout, expectedChordLayout) |
|
|
}) |
|
|
|
|
|
it("staccato-placement", function() { |
|
|
doItemPlacementTest(abcStaccatoPlacement, expectedStaccatoPlacement, '[data-name="scripts.staccato"]'); |
|
|
}) |
|
|
|
|
|
it("rhythm-placement", function() { |
|
|
doItemPlacementTest(abcRhythmPlacement, expectedRhythmPlacement, '[data-name="clefs.G"]'); |
|
|
}) |
|
|
|
|
|
it("accent-spacing", function() { |
|
|
doItemPlacementTest(accentSpacing, expectedAccentSpacing, '[data-name="lyric"]'); |
|
|
}) |
|
|
|
|
|
it("measure-numbers", function() { |
|
|
doItemPlacementTest(abcBarLinesTreble, expectedBarLinesTreble, '[data-name="bar-number"]'); |
|
|
doItemPlacementTest(abcBarLinesBass, expectedBarLinesBass, '[data-name="bar-number"]'); |
|
|
}) |
|
|
|
|
|
it("equal-spacing", function() { |
|
|
abcjs.renderAbc("paper", equalSpacing, {add_classes: true, timeBasedLayout:{minPadding: 5, minWidth: 1200}}); |
|
|
var svg = document.querySelector('#paper svg') |
|
|
var OFFSET = 50 |
|
|
var SPACING = 291.48725/4 |
|
|
for (var i = 0; i < 17; i++) { |
|
|
const line = document.createElementNS("http://www.w3.org/2000/svg", "line"); |
|
|
line.setAttribute("x1", ''+(OFFSET+SPACING*i)); |
|
|
line.setAttribute("y1", ''+90); |
|
|
line.setAttribute("x2", ''+(OFFSET+SPACING*i)); |
|
|
line.setAttribute("y2", ''+510); |
|
|
line.setAttribute("stroke", "#0000ff50"); |
|
|
svg.appendChild(line); |
|
|
} |
|
|
}) |
|
|
|
|
|
}) |
|
|
|
|
|
function doItemPlacementTest(abc, expected, selector) { |
|
|
abcjs.renderAbc("paper", abc, {add_classes: true}); |
|
|
var els = document.querySelectorAll("#paper "+selector) |
|
|
var pos = [] |
|
|
for (var i = 0; i < els.length; i++) { |
|
|
var bb = els[i].getBBox() |
|
|
pos.push({x: Math.round(bb.x), y: Math.round(bb.y)}) |
|
|
} |
|
|
|
|
|
chai.assert.deepEqual(pos, expected) |
|
|
} |
|
|
|
|
|
function doChordLayoutTest(abc, expected) { |
|
|
var visualObj = abcjs.renderAbc("paper", abc, { showDebug: "box", add_classes: true, staffwidth: 260, format: { gchordfont: "20"}}); |
|
|
var els = document.querySelectorAll("#paper .abcjs-chord") |
|
|
var pos = [] |
|
|
for (var i = 0; i < els.length; i++) { |
|
|
var bb = els[i].getBBox() |
|
|
pos.push({x: Math.round(bb.x), y: Math.round(bb.y)}) |
|
|
} |
|
|
console.log(pos) |
|
|
chai.assert.deepEqual(pos, expected) |
|
|
} |
|
|
|
|
|
function doCollidingNotesTest(abc, expected) { |
|
|
var visualObj = abcjs.renderAbc("paper", abc, {}); |
|
|
var results = []; |
|
|
var errors = []; |
|
|
for (var ln = 0; ln < visualObj[0].lines.length; ln++) { |
|
|
var line = visualObj[0].lines[ln] |
|
|
if (line.staffGroup && line.staffGroup.voices) { |
|
|
if (line.staffGroup.voices.length > 1) { |
|
|
for (var v = 0; v < line.staffGroup.voices.length; v++) { |
|
|
if (!results[v]) results[v] = []; |
|
|
var voice = line.staffGroup.voices[v]; |
|
|
for (var e = 0; e < voice.children.length; e++) { |
|
|
var el = voice.children[e] |
|
|
var r = { w: el.w, dx: el.children[0].dx} |
|
|
results[v].push(r) |
|
|
if (!expected[v] || !expected[v][e]) |
|
|
console.log(v, e, r) |
|
|
if (expected[v][e].w !== r.w || expected[v][e].dx !== r.dx) |
|
|
errors.push({v: v, e: e, exp: expected[v][e], rcv: r}) |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
console.log(JSON.stringify(results)) |
|
|
if (errors.length > 0) |
|
|
chai.assert.equal(true, false, JSON.stringify(errors, null, " ")); |
|
|
|
|
|
} |
|
|
|
|
|
function doLayoutTest(abc, params, expected, comment) { |
|
|
var visualObj = abcjs.renderAbc("paper", abc, params); |
|
|
if (params.staffwidth) |
|
|
verticalLine("#paper svg", params.staffwidth+15) |
|
|
var result = []; |
|
|
for (var j = 0; j < visualObj[0].lines.length; j++) { |
|
|
var arr = []; |
|
|
if (visualObj[0].lines[j].staff) { |
|
|
for (var i = 0; i < visualObj[0].lines[j].staff[0].voices[0].length; i++) { |
|
|
var el = visualObj[0].lines[j].staff[0].voices[0][i]; |
|
|
arr.push(Math.round(el.abselem.x)); |
|
|
} |
|
|
} else if (visualObj[0].lines[j].nonMusic && visualObj[0].lines[j].nonMusic.rows) { |
|
|
for (var k = 0; k < visualObj[0].lines[j].nonMusic.rows.length; k++) { |
|
|
var row = visualObj[0].lines[j].nonMusic.rows[k] |
|
|
if (row.left) |
|
|
arr.push(Math.round(row.left)) |
|
|
} |
|
|
} |
|
|
result.push(arr); |
|
|
} |
|
|
for (j = 0; j < result.length; j++) { |
|
|
var recv = result[j]; |
|
|
var exp = expected[j]; |
|
|
var msg = comment + "\nrcv: " + JSON.stringify(recv) + "\n" + |
|
|
"exp: " + JSON.stringify(exp) + "\n"; |
|
|
chai.assert.deepStrictEqual(recv, exp, msg); |
|
|
} |
|
|
|
|
|
|
|
|
return visualObj |
|
|
} |
|
|
|
|
|
function verticalLine(selector, x) { |
|
|
var svgNS = "http://www.w3.org/2000/svg"; |
|
|
var cursor = document.createElementNS(svgNS, "line"); |
|
|
cursor.setAttribute("class", "abcjs-cursor"); |
|
|
cursor.setAttributeNS(null, 'x1', x); |
|
|
cursor.setAttributeNS(null, 'y1', 0); |
|
|
cursor.setAttributeNS(null, 'x2', x); |
|
|
cursor.setAttributeNS(null, 'y2', 800); |
|
|
cursor.setAttributeNS(null, 'stroke', "blue"); |
|
|
var svg = document.querySelector(selector); |
|
|
svg.appendChild(cursor); |
|
|
|
|
|
} |
|
|
|