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

Reply via email to