Attached to this post there's the improved version (fixed indentation and
lines' max length).
Note too that this version includes a function that prevents opening the
default menu with the right mouse button.

Can someone fix the scheme code indentation?
After that, can we add this on the snippets repository?



On Sun, Dec 15, 2019 at 8:04 AM Werner LEMBERG <w...@gnu.org> wrote:

>
> > At this point, here's the complete all-in-one .ly template.  [...]
>
> It would be nice if you could format this to avoid line lengths longer
> than 80 characters.  As the non-HTML part of your e-mail shows, the
> code becomes very hard to read otherwise.
>
>
>     Werner
>
\version "2.19.45"

JSSVGSlurTuner = #(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"))))))

JSSVGSlurTunerScript = #"
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 slurId = 0
var currCp = null

function setCpsOnPath(path, x1, y1, x2, y2, x3, y3, x4, y4) {

	path.setAttribute('d', 'M ' + x1 + ',' + y1 + ' ' +
										'C ' + x2 + ',' + y2 + ' ' +
										x3 + ',' + y3 + ' ' +
										x4 + ',' + y4)

}

function initSlur(node) {

	//already initialized
	if (node.hasAttribute('id'))
		return

	cpsCounter = 1
	lineCounter = 1

	for (n1 = node.firstChild; n1 !== null; n1 = n1.nextSibling) {
		if (n1.nodeName == 'g') {
			for (n2 = n1.firstChild; n2 !== null; n2 = n2.nextSibling) {
			
				if (n2.nodeName == 'circle') {
					if (!node.hasAttribute('cp' + cpsCounter + 'x')) {
						//TODO Ugly parsering, replace with a proper and safer one
						transf = n2.getAttribute('transform')
						node.setAttribute('cp' + cpsCounter + 'x',
															transf.replace('translate(', '').split(',')[0])
						node.setAttribute('cp' + cpsCounter + 'y',
															transf.split(',')[1].trim().replace(')', ''))
						n2.setAttribute('id', slurId + '_cp_' + cpsCounter)
						n2.setAttribute('onmousedown', 'selectCp(this)')
					}
					cpsCounter++
				}

				if (n2.nodeName == 'line') {
					n2.setAttribute('id', slurId + '_line_' + lineCounter)
					lineCounter++
				}
			}
		}

		//remove 'transform' attribute and set abs coords
		if (n1.nodeName == 'path') {
			if (n1.hasAttribute('transform'))
				n1.removeAttribute('transform')

			setCpsOnPath(n1,
                   node.getAttribute('cp1x'),
                   node.getAttribute('cp1y'),
                   node.getAttribute('cp2x'),
                   node.getAttribute('cp2y'),
                   node.getAttribute('cp3x'),
                   node.getAttribute('cp3y'),
                   node.getAttribute('cp4x'),
                   node.getAttribute('cp4y'))

			n1.setAttribute('id', slurId + '_path')
			n1.setAttribute('fill', 'none')
		}
	}

	node.setAttribute('id', slurId)

	coords = document.createElementNS('http://www.w3.org/2000/svg', 'text')
	coords.setAttribute('transform',
											'translate('+ node.getAttribute('cp1x') + ',' + 
																		node.getAttribute('cp1y') + ')')
	coords.setAttribute('font-size', '2')
	coords.setAttribute('class', 'lilySlurCoords')
	coords.setAttribute('id', slurId + '_coords')
	node.appendChild(coords)

	slurId++

}

function selectCp(cp) {

	if (!cp.hasAttribute('id'))
		return

	if (!detectLeftButton(event)) {
		event.preventDefault()
		showCoords(cp.getAttribute('id').split('_')[0])
		return
	}

	cp.setAttribute('color', 'cyan')
	currCp = cp

}

function unselectCp() {

	if (currCp)
		currCp.setAttribute('color', 'rgb(255, 127, 0)')

	currCp = null
	
}

function moveCp() {

	if (!currCp)
		return

	translateCoordsStr = event.pageX * scaleX + ',' + event.pageY * scaleY
	currCp.setAttribute('transform',
  										'translate(' + translateCoordsStr + ')')

	//get the associated path
	assocSlurId = currCp.getAttribute('id').split('_')[0]
	path = document.getElementById(assocSlurId + '_path')

	xs = []
	ys = []

	for (i = 0; i < 4; i++) {
		cpElem = document.getElementById(assocSlurId + '_cp_' + (i + 1))
		transf = cpElem.getAttribute('transform')

		xs[i] = transf.replace('translate(', '')
		xs[i] = xs[i].split(',')[0]
		
		ys[i] = transf.split(',')[1].trim().replace(')', '')
	}

	for (i = 0; i < 3; i++) {
		currLine = document.getElementById(assocSlurId + '_line_' + (i + 1))
		currLine.setAttribute('transform', 
													'translate(' + xs[i] + ',' + ys[i] + ')')
		currLine.setAttribute('x2', xs[i + 1] - xs[i])
		currLine.setAttribute('y2', ys[i + 1] - ys[i])
	}

	setCpsOnPath(path, xs[0], ys[0], xs[1], ys[1], xs[2], ys[2], xs[3], ys[3])

}

function showCoords(assocSlurId) {

	coordsToDisplay = '\\\\shape #\\'('
	assocSlur = document.getElementById(assocSlurId)

	for (q = 0; q < 4; q++) {
		newCp = document.getElementById(assocSlurId + '_cp_' + (q + 1))
		
		xsOrig = assocSlur.getAttribute('cp' + (q + 1) + 'x')
		
		xsNew = newCp.getAttribute('transform')
		xsNew = xsNew.replace('translate(', '').split(',')[0]
		
		ysOrig = assocSlur.getAttribute('cp' + (q + 1) + 'y')
		
		ysNew = newCp.getAttribute('transform').split(',')[1]
		ysNew = ysNew.trim().replace(')', '')
		
		diffX = (xsNew - xsOrig).toFixed(3)
		diffY = (ysOrig - ysNew).toFixed(3)
		
		coordsToDisplay += '(' + (+diffX) + ' . ' + (+diffY) + ') '
	}

	coordsToDisplay = coordsToDisplay.substring(0, coordsToDisplay.length - 1)
	coordsToDisplay += ') ' + assocSlur.getAttribute('slurtype')

	alert(coordsToDisplay)

}

function detectLeftButton(evt) {

	evt = evt || window.event
	if ('buttons' in evt) {
		return evt.buttons == 1
	}
	var button = evt.which || evt.button
	return button == 1

}

window.oncontextmenu = function (evt) {

	evt.preventDefault()

}

var as = document.querySelectorAll('a')

//Remove all 'a' tags
for (var i = 0; i < as.length; i++) {
	as[i].replaceWith(...as[i].childNodes)
}

slurs = document.querySelectorAll('svg .lilySlur')

for (i = 0; i < slurs.length; i++) {
	initSlur(slurs[i])
}

document.addEventListener('mouseup', unselectCp)
document.addEventListener('mousemove', moveCp)
"	

addJSSVGSlurTuner = \JSSVGSlurTuner \JSSVGSlurTunerScript

addControlPoints = #(grob-transformer 'stencil (lambda (grob orig)
  (define (draw-control-point pt)
#{ \markup \translate $pt \with-color #'(1 .7 .7) \draw-circle #0.6 #0 ##t #})
  (define (draw-control-segment pt1 pt2)
(let ((delta (cons (- (car pt2) (car pt1)) (- (cdr pt2) (cdr pt1))))) #{ \markup \translate $pt1 \with-color #'(1 .5 0) \draw-line $delta #}))
  (let* ((pts (ly:grob-property grob 'control-points))
         (dots (map (lambda (pt)
            (grob-interpret-markup grob (draw-control-point pt)))
            pts))
         (lines (map (lambda (pt1 pt2)
            (grob-interpret-markup grob (draw-control-segment pt1 pt2)))
            pts (cdr pts))))
    (ly:stencil-add
      (apply ly:stencil-add lines)
      (apply ly:stencil-add dots)
      orig))))

showControlPoints = {
    \override PhrasingSlur.stencil = #addControlPoints
    \override PhrasingSlur.output-attributes = #'((class . "lilySlur")(slurtype . "PhrasingSlur"))
    \override Slur.stencil = #addControlPoints
    \override Slur.output-attributes = #'((class . "lilySlur")(slurtype . "Slur"))
    \override Tie.stencil = #addControlPoints
    \override Tie.output-attributes = #'((class . "lilySlur")(slurtype . "Tie"))  
}

%% How it works: 
%% 
%% 1) Produce SVG output file(s) ( compile with -dbackend=svg ) 
%% 2) Open the SVG file(s) with any viewer (it works with Firefox and Chrome browsers too)
%% 3) Modify slurs/ties with the mouse by moving their control points
%% 4) Right click on one of the control points of a modified slur/tie, 
%%    and copy and paste the expression generated by the script to the .ly file, just before the corresponding slur/tie. Recompile.

\score
{
    {
        \showControlPoints

        g4_\( a' 

        b'2~ | 

        b'2( e''8 d'') c'4\) 
    }
    
}

\addJSSVGSlurTuner

Reply via email to