Hello everybody, here is another snippet that uses the system that I previously implemented. This time for the tuning of the beams. This is too a very tedious operation to do with just the code and for this I believe that this tool is also useful. As you can see, the code is not only shorter than the previous one, but it recycles the whole template of the previous snippet, which I'm making more and more generic. In short, the goal is to have Lilypond itself automatically create a GUI tool for the tuning of the main properties of the grobs.
This is somewhat crazy: Lilypond used as a factory for WYSIWYG editors! :-) HTH Paolo P.S) I don't know why the browser's viewer messes up the indentation of these attachments. If so, I ask if are there volunteers to fix that and re-post the snippet (I see correct indentation if I paste the code to any online js editor)
\version "2.19.45" JSSVGBeamTuner = #(define-void-function (body) (string?) (let* ((mod (resolve-module '(scm framework-svg))) (svg-end (module-ref mod 'svg-end #f))) (if (procedure? svg-end) (module-define! mod 'svg-end (lambda () (string-join (list "<script type=\"text/javascript\"><![CDATA[" body "]]></script>" (svg-end)) "\n")))))) JSSVGBeamTunerScript = #" rootNode = document.querySelector('svg') pixelsX = rootNode.getAttribute('width').replace('mm', '') * 96 / 25.4 pixelsY = rootNode.getAttribute('height').replace('mm', '') * 96 / 25.4 scaleX = rootNode.getAttribute('viewBox').split(' ')[2] / pixelsX scaleY = rootNode.getAttribute('viewBox').split(' ')[3] / pixelsY var beamId = 0 var currGrob = null function pointsArrToStr(points) { pointsStr = '' for (i = 0; i< 8; i++) { pointsStr += points[i] + ' ' } return pointsStr.substring(0, pointsStr.length - 1) } function initBeam(n1) { for (n2 = n1.firstChild; n2 !== null; n2 = n2.nextSibling) { if (n2.nodeName == 'polygon') { //TODO Ugly parsering, replace with a proper and safer one transf = n2.getAttribute('transform') // TODO: check backspaces? // TODO: check if it doesn't have transform attr? translateX = transf.replace('translate(', '').split(',')[0] translateY = transf.split(',')[1].trim().replace(')', '') points = n2.getAttribute('points').split(' ') //alert(translateX) for (i = 0; i<7; i = i+2) { points[i] = Number(points[i]) + Number(translateX) } for (i = 1; i<8; i = i+2) { points[i] = Number(points[i]) + Number(translateY) } pointsStr = pointsArrToStr(points) n2.setAttribute('points', pointsStr) n2.setAttribute('origPoints', pointsStr) n2.setAttribute('id', beamId++) n2.setAttribute('onmousedown', 'selectGrob(this)') n2.removeAttribute('transform') n2.setAttribute('anchor', '') } } } function selectGrob(grob) { if (!grob.hasAttribute('id')) return if (!detectLeftButton(event)) { event.preventDefault() showGrobModifyExpr(grob) return } grob.setAttribute('color', 'cyan') points = grob.getAttribute('points').split(' ') leftX = Number(points[4]) rightX = Number(points[0]) distanceFromRightSide = Math.abs(event.pageX * scaleX - rightX) distanceFromLeftSide = Math.abs(event.pageX * scaleX - leftX) distanceFromCenter = Math.abs(event.pageX * scaleX - (leftX + (rightX - leftX)/2)) minDistance = Math.min(distanceFromLeftSide, distanceFromCenter, distanceFromRightSide) switch(minDistance) { case distanceFromRightSide: grob.setAttribute('anchor','right') break; case distanceFromLeftSide: grob.setAttribute('anchor','left') break; case distanceFromCenter: grob.setAttribute('anchor','center') break; } grob.setAttribute('yScreenPrev', event.pageY * scaleY) currGrob = grob } function detectLeftButton(evt) { evt = evt || window.event if ('buttons' in evt) { return evt.buttons == 1 } var button = evt.which || evt.button return button == 1 } function unselectGrob() { if (currGrob) currGrob.setAttribute('color', 'black') currGrob = null } function moveGrob() { if (!currGrob) return points = currGrob.getAttribute('points').split(' ') origPoints = currGrob.getAttribute('origPoints').split(' ') idx1 = 1 idx2 = 8 if (currGrob.getAttribute('anchor') == 'left') { idx1 = 5 } else if (currGrob.getAttribute('anchor') == 'right') { idx2 = 5 } for (i = idx1; i < idx2; i = i + 2) { points[i] = Number(points[i]) + event.pageY * scaleY - Number(currGrob.getAttribute('yScreenPrev')) } console.log (points[1]) currGrob.setAttribute('yScreenPrev', event.pageY * scaleY) currGrob.setAttribute('points', pointsArrToStr(points)) } window.oncontextmenu = function(evt) { evt.preventDefault() } function showGrobModifyExpr(grob) { angleLeft = +((grob.getAttribute('origPoints').split(' ')[5] - grob.getAttribute('points').split(' ')[5]).toFixed(3)) angleRight = +((grob.getAttribute('origPoints').split(' ')[1] - grob.getAttribute('points').split(' ')[1]).toFixed(3)) lilyExpr = '-\\\\offset positions ' + '#\\'(' + angleLeft + ' . ' + angleRight + ')' alert(lilyExpr) } var as = document.querySelectorAll('a') //Remove all 'a' tags for (var i = 0; i < as.length; i++) { as[i].replaceWith(...as[i].childNodes) } beams = document.querySelectorAll('svg .lilyBeam') for (i = 0; i < beams.length; i++) { initBeam(beams[i]) } document.addEventListener('mouseup', unselectGrob) document.addEventListener('mousemove', moveGrob) " addJSSVGBeamTuner = \JSSVGBeamTuner \JSSVGBeamTunerScript addSVGBeamHandler = { \override Beam.output-attributes = #'((class . "lilyBeam")) } \score { { \addSVGBeamHandler % How-to: % 1) move with the left mouse button the angles or the vertical position % of the beam in the SVG output file. % 2) right-click and replace the line below with the output string in this way: % a8-\offset positions #'(y1 . y2)[ c' c' c'] c' d' e' f' a8[ c' c' c'] c' d' e' f' } } \addJSSVGBeamTuner