Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Auto measure label handling #302

Open
wants to merge 61 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
46b9420
auto join measure labels if point to same zone
riedde Dec 24, 2022
293159d
fix measure label join with multirests
riedde Dec 24, 2022
b962381
add diacrtical aspects to measure labels
riedde Dec 27, 2022
58f67ce
add css for deleted measures
riedde Dec 27, 2022
461e631
introduce module for measure actions
riedde Dec 27, 2022
271543c
fix default return type
riedde Dec 27, 2022
47d5278
add functx namespace
riedde Mar 13, 2023
e59e582
introduce handling for reg
riedde Mar 13, 2023
3911b17
Merge branch 'develop' into ftr/measure-label-handling
riedde Feb 7, 2024
1a4d91d
measure: move local functions to module
riedde Feb 7, 2024
74d16bc
Merge branch 'develop' into ftr/measure-label-handling
bwbohl Feb 25, 2024
9b068a7
add and format structuring comments, XQdoc comments
bwbohl Feb 26, 2024
119d928
apply current XQuery style guide
bwbohl Feb 26, 2024
d7da75b
fix return tpe to proper JSON
bwbohl Feb 26, 2024
2ec2790
Merge branch 'develop' into ftr/measure-label-handling
riedde Jun 21, 2024
14837c2
adding else case if no multiple measures point to a zone
riedde Jun 21, 2024
43d8b28
Merge branch 'develop' into ftr/measure-label-handling
riedde Sep 18, 2024
59e2af5
Merge branch 'develop' into ftr/measure-label-handling
riedde Nov 6, 2024
7e614ff
Merge branch 'develop' into ftr/measure-label-handling
riedde Nov 7, 2024
09974a5
add first test for the measure module
peterstadler Nov 8, 2024
9b2d51e
fixing broken syntax
riedde Nov 7, 2024
62cc4d4
adding documentation and return types #302
riedde Nov 13, 2024
e9fa11f
Merge branch 'develop' into ftr/measure-label-handling
riedde Nov 13, 2024
2beae37
extend handling for mei:perfRes attributes
riedde Nov 13, 2024
da9a7ec
need to catch errors and failures
peterstadler Nov 15, 2024
ce0b55a
add initial test for `measure:resolveMultiMeasureRests#2`
peterstadler Nov 15, 2024
ae98754
Update add/data/xqm/measure.xqm
riedde Nov 23, 2024
f0dc4b6
Update add/data/xqm/measure.xqm
riedde Nov 23, 2024
c3971d7
Update add/data/xqm/measure.xqm
riedde Nov 23, 2024
006fe7b
Update add/data/xqm/measure.xqm
riedde Nov 23, 2024
537bf5e
Update add/data/xqm/measure.xqm
riedde Dec 4, 2024
7f89d50
small adjustments following suggestions of the code review
riedde Dec 4, 2024
d81ae8a
Merge branch 'ftr/measure-label-handling' of github.com:Edirom/Edirom…
riedde Dec 4, 2024
b1bcda6
Update add/data/xqm/measure.xqm
riedde Dec 10, 2024
5915413
Update add/data/xqm/measure.xqm
riedde Dec 10, 2024
c8ec168
revert 7f89d50c0325b1fc77daf65c20407236eef61ed9 partially
riedde Dec 10, 2024
a7a604f
adding xhtml namespace to created elements
riedde Dec 10, 2024
5522f8a
update variable names, remove obsolete loop
riedde Dec 10, 2024
b2456dc
Merge branch 'develop' into ftr/measure-label-handling
riedde Dec 10, 2024
8708dcc
fix syntax error
riedde Dec 10, 2024
741a51a
add fallback (key not found)
riedde Dec 10, 2024
13c8c8b
fix namespace and module location
riedde Dec 10, 2024
e5dc953
add extended handling for perfRes labeling
riedde Dec 10, 2024
5452ba6
Update add/data/xqm/measure.xqm
riedde Dec 10, 2024
6946585
Update add/data/xqm/measure.xqm
riedde Dec 10, 2024
2acb3b2
Update add/data/xqm/measure.xqm
riedde Dec 10, 2024
91349fa
Update add/data/xqm/measure.xqm
riedde Dec 10, 2024
07141a9
Update add/data/xqm/measure.xqm
riedde Dec 10, 2024
def111d
Update testing/XQSuite/measure-tests.xqm
riedde Dec 10, 2024
77ddd9c
Update add/data/xqm/measure.xqm
riedde Dec 10, 2024
88212eb
Update add/data/xqm/measure.xqm
riedde Dec 10, 2024
4d745b9
add test for `getMeasureLabel#1`
peterstadler Dec 11, 2024
89b86cd
remove import for unused module
riedde Dec 16, 2024
2ad0a2d
remove brackets and comment
riedde Dec 16, 2024
4eefd32
changed strings-maps to json-maps
riedde Dec 16, 2024
01568d2
return array instead of maps* #302
riedde Dec 17, 2024
45082e6
refactor `measure:analyzeLabel#1`
peterstadler Dec 17, 2024
d72d013
properly return a JSON array
peterstadler Dec 17, 2024
decd562
comment out not-working and not needed code
peterstadler Dec 18, 2024
c9ed32f
comment out test for disabled function
peterstadler Dec 18, 2024
b45c7b5
Merge branch 'develop' into ftr/measure-label-handling
peterstadler Dec 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions add/data/xql/getMeasurePage.xql
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ declare function local:findMeasure($mei, $movementId, $measureIdName) as element
else
(($mei/id($movementId)//mei:measure[@label eq $measureIdName], $mei/id($movementId)//mei:measure[@n eq $measureIdName])[1])
};

declare function local:getMeasure($mei, $measure, $movementId) as map(*) {
let $measureId := $measure/string(@xml:id)
let $zoneId := substring-after($measure/string(@facs), '#')
Expand Down
69 changes: 9 additions & 60 deletions add/data/xql/getMeasuresOnPage.xql
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@ xquery version "3.1";
:)

(:~
Returns a JSON sequence with all measures on a specific page.

@author <a href="mailto:[email protected]">Daniel Röwenstrunk</a>
Returns a JSON array with all measures on a specific page.
:)

(: IMPORTS ================================================================= :)

import module namespace eutil="http://www.edirom.de/xquery/eutil" at "/db/apps/Edirom-Online/data/xqm/eutil.xqm";

import module namespace measure = "http://www.edirom.de/xquery/measure" at "/db/apps/Edirom-Online/data/xqm/measure.xqm";

(: NAMESPACE DECLARATIONS ================================================== :)

declare namespace mei = "http://www.music-encoding.org/ns/mei";
Expand All @@ -22,72 +26,17 @@ declare namespace xmldb = "http://exist-db.org/xquery/xmldb";
declare option output:method "json";
declare option output:media-type "application/json";

(: FUNCTION DECLARATIONS =================================================== :)

(:~
Finds all measures on a page.

@param $mei The sourcefile
@param $surface The surface to look at
@returns A list of json objects with measure information
:)
declare function local:getMeasures($mei as node(), $surface as node()) as map(*)* {

for $zone in $surface/mei:zone[@type = 'measure']
let $zoneRef := concat('#', $zone/@xml:id)
(:
The first predicate with `contains` is just a rough estimate to narrow down the result set.
It uses the index and is fast while the second (exact) predicate is generally too slow
:)
let $measures := $mei//mei:measure[contains(@facs, $zoneRef)][$zoneRef = tokenize(@facs, '\s+')]
return
for $measure in $measures
let $measureLabel :=
if ($measure/@label) then
($measure/string(@label))
else
($measure/string(@n))
let $measureLabel :=
if ($measure//mei:multiRest) then
($measureLabel || '–' || number($measureLabel) + number($measure//mei:multiRest/@num) - 1)
else
($measureLabel)
return
map {
'zoneId': $zone/string(@xml:id),
'ulx': $zone/string(@ulx),
'uly': $zone/string(@uly),
'lrx': $zone/string(@lrx),
'lry': $zone/string(@lry),
'id': $measure/string(@xml:id),
'name': $measureLabel,
'type': $measure/string(@type),(: where is measure type being used :)
'rest': local:getMRest($measure)
}
};

declare function local:getMRest($measure) {

if ($measure//mei:mRest) then
(string('1'))
else if ($measure//mei:multiRest) then
($measure//mei:multiRest/string(@num))
else
(string('0'))
};

(: QUERY BODY ============================================================== :)

let $uri := request:get-parameter('uri', '')

let $surfaceId := request:get-parameter('pageId', '')

let $mei := doc($uri)/root()
let $mei := eutil:getDoc($uri)

let $surface := $mei/id($surfaceId)

return
array {
(: TODO: überlegen, wie die Staff-spezifischen Ausschnitte angezeigt werden sollen :)
local:getMeasures($mei, $surface)
measure:getMeasuresOnPage($mei, $surface)
}
16 changes: 12 additions & 4 deletions add/data/xqm/eutil.xqm
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ declare function eutil:getDocumentLabel($doc as xs:string, $edition as xs:string
declare function eutil:getPartLabel($measureOrPerfRes as node(), $type as xs:string) as xs:string {

(: request:get-parameter('lang', '') doesn't work?? [DeRi]:)
(: replace with eutil:getLanguage($edition) or similar:)

let $lang :=
if(request:get-parameter('lang', '') = '') then
Expand All @@ -214,12 +215,17 @@ declare function eutil:getPartLabel($measureOrPerfRes as node(), $type as xs:str
let $part := $measureOrPerfRes/ancestor::mei:part
let $voiceRef := $part//mei:staffDef/@decls
let $voiceID := substring-after($voiceRef, '#')
let $voiceElem := $measureOrPerfRes/ancestor::mei:mei/id($voiceID)

let $perfResLabel :=
if($type eq 'measure') then
($measureOrPerfRes/ancestor::mei:mei/id($voiceID)/@label)
else
if($type eq 'measure' and $voiceElem[@label]) then
($voiceElem/@label)
else if($type eq 'measure' and $voiceElem[@codedval]) then
($voiceElem/@codedval)
else if ($measureOrPerfRes[@label]) then
($measureOrPerfRes/@label)
else
($measureOrPerfRes/@codedval)

let $dictKey := 'perfMedium.perfRes.' || functx:substring-before-if-contains($perfResLabel,'.')

Expand Down Expand Up @@ -266,7 +272,9 @@ declare function eutil:getLanguageString($key as xs:string, $values as xs:string
let $base := system:get-module-load-path()
let $file := eutil:getDoc(concat($base, '/../locale/edirom-lang-', $lang, '.xml'))

let $string := $file//entry[@key = $key]/string(@value)
let $string := if($file//entry[@key = $key]) then (
$file//entry[@key = $key]/string(@value)
) else('noValueFound')
let $string := functx:replace-multi($string, for $i in (0 to (count($values) - 1)) return concat('\{',$i,'\}'), $values)

return
Expand Down
236 changes: 236 additions & 0 deletions add/data/xqm/measure.xqm
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
xquery version "3.1";
(:
: For LICENSE-Details please refer to the LICENSE file in the root directory of this repository.
:)

(:~
: This module provides library functions for Sources
:
: @author Dennis Ried
:)
module namespace measure = "http://www.edirom.de/xquery/measure";

(: IMPORTS ================================================================= :)

import module namespace functx="http://www.functx.com";

import module namespace eutil="http://www.edirom.de/xquery/eutil" at "/db/apps/Edirom-Online/data/xqm/eutil.xqm";

(: NAMESPACE DECLARATIONS ================================================== :)

declare namespace mei="http://www.music-encoding.org/ns/mei";
declare namespace xhtml="http://www.w3.org/1999/xhtml";

(: FUNCTION DECLARATIONS =================================================== :)

(:~
: Returns a label for a measure, taken either from the `@label` attribute
: if present or the `@n` attribute otherwise.
:
: @param $measure The measure to be processed
: @return the string value of the `@label` or the `@n` attribute if present, the empty sequence otherwise.
:)
declare function measure:getMeasureLabelAttr($measure as element(mei:measure)) as xs:string? {

if(exists($measure[@label])) then (
$measure/@label => data()
) else (
$measure/@n => data()
)
};

(:~
: Adds diacritical characters
:
: @param $measure The measure to be processed
: @param $measureLabel The label of the measure
: @return A span containing the label
:)
declare function measure:makeMeasureLabelCritical($measure as node(), $measureLabel as xs:string) as element(xhtml:span) {

let $measureParentElem := local-name($measure/parent::node())
return
if($measureParentElem = 'supplied') then (
<xhtml:span class="supplied">{'[' || $measureLabel || ']'}</xhtml:span>
) else if($measureParentElem = 'del') then (
<xhtml:span class="del">{$measureLabel}</xhtml:span>
) else (
<xhtml:span>{$measureLabel}</xhtml:span>
)
};

(:~
: Returns a label for a measure (range)
:
: @param $measure The measure to be processed
: @return A span containing the label
:)
declare function measure:getMeasureLabel($measure as element(mei:measure)) as element(xhtml:span) {

let $measureLabel := measure:getMeasureLabelAttr($measure)
let $measureID := $measure/@xml:id
let $measureFacs := $measure/@facs
let $measuresZoneRef := $measure/ancestor::mei:mdiv//mei:measure[@facs = $measureFacs]
let $measureZoneRefCount := count($measuresZoneRef)

let $measureLabels :=
if(not($measure/parent::mei:reg)) then (
if(($measureZoneRefCount gt 1) and ($measure//mei:multiRest)) then (
for $measure in $measuresZoneRef
let $measureLabel := measure:getMeasureLabelAttr($measure)
let $measureLabel := measure:makeMeasureLabelCritical($measure, $measureLabel)
let $measureLabel := ($measureLabel || '–' || number($measureLabel) + number($measure//mei:multiRest/@num) - 1)
return
<xhtml:span>{$measureLabel}</xhtml:span>
) else if ($measureZoneRefCount gt 1) then (
for $label in $measuresZoneRef
return
measure:makeMeasureLabelCritical($label, $label/string(@label))
) else if ($measure//mei:multiRest) then (
<xhtml:span>{$measureLabel || '–' || (number($measureLabel) + number($measure//mei:multiRest/@num) - 1)}</xhtml:span>
) else (
measure:makeMeasureLabelCritical($measure, $measureLabel)
)
) else if($measure/parent::mei:reg) then (
for $measure in $measuresZoneRef
return
measure:getRegMeasureLabel($measure/parent::mei:reg)
) else (
<xhtml:span>noLabel</xhtml:span>
)

return
measure:joinMeasureLabels($measureLabels)
};

(:~
: Returns a label for a measure (range)
:
: @param $labels The measure to be processed
: @return A span containing the joined label
:)
declare function measure:joinMeasureLabels($labels as node()*) as element(xhtml:span) {

<xhtml:span>{
for $label at $pos in functx:distinct-deep($labels)
return
if($pos = 1) then (
$label
) else(
'/',$label
)
}</xhtml:span>
};

(:~
: Returns a label for a regularized measure (range)
:
: @param $reg The reg element (containing measure) to be processed
: @return The label as string
:)
declare function measure:getRegMeasureLabel($reg as node()) as element(xhtml:span) {

let $measures := $reg/mei:measure
let $measuresCount := count($measures)
let $measureStart := $measures[1]
let $measureStop := $measures[$measuresCount]
let $measureLabel := measure:getMeasureLabelAttr($measureStart) || '–' || measure:getMeasureLabelAttr($measureStop)
return
<xhtml:span>{$measureLabel}</xhtml:span>
};

(:~
: Returns the value of a (multi) measure rest
:
: @param $measure The measure to be processed
: @return The value of the rest as string
:)
declare function measure:getMRest($measure as element(mei:measure)) as xs:string {

if($measure//mei:mRest) then (
string('1')
) else if($measure//mei:multiRest) then(
$measure//mei:multiRest/string(@num)
) else (
string('0')
)
};

(:~
: Returns resolved labels
: E.g., for a measure with the label "3-5" this function will return "3,4,5";
: for a measure with the n attribute "4a" this function will return "4a".
:
: @param $measure The measure to be processed
: @return A sequence of measure numbers or labels
:)
declare function measure:analyzeLabel($measure as element(mei:measure)) as xs:string* {
if($measure/@label)
then (
if (matches($measure/@label, '^\d+[\-–]\d+$'))
then (
let $first := functx:substring-before-match($measure/@label, '[\-–]') => number()
let $last := functx:substring-after-match($measure/@label, '[\-–]') => number()
let $steps := ($last - $first + 1) => xs:integer()
for $i in 1 to $steps
return
string($first + $i - 1)
)
else $measure/@label => data()
)
else $measure/@n => data()
};

(:~
: Resolvs multi-measure rests
:
: @param $mdiv The mdiv to be processed
: @param $measureN The number of the measure to be resolved
: @return The measures covered by the multi-measure rest
:)
(: This does not work, see discussion at https://github.com/Edirom/Edirom-Online/pull/302 :)
(:
declare function measure:resolveMultiMeasureRests($mdiv as node(), $measureN as xs:string) as node()* {
let $measureNNumber := number($measureN)
return
if ($mdiv//mei:measure[.//mei:multiRest][@label]) then (
$mdiv//mei:measure[.//mei:multiRest][number(substring-before(@label, '–')) <= $measureNNumber][.//mei:multiRest/number(@num) gt number($measureNNumber - substring-before(@label, '–'))]
) else (
$mdiv//mei:measure[.//mei:multiRest][number(@n) lt $measureNNumber][.//mei:multiRest/number(@num) gt ($measureNNumber - number(@n))]
riedde marked this conversation as resolved.
Show resolved Hide resolved
)
};
:)

(:~
: Finds all measures on a page.
:
: @param $mei The sourcefile
: @param $surface The surface to look at
: @returns A sequence of map objects with measure information
:)
declare function measure:getMeasuresOnPage($mei as document-node()?, $surface as element(mei:surface)) as map(*)* {

for $zone in $surface/mei:zone[@type='measure'][@xml:id]
(: do we need to compute an id for zones without @xml:id? :)
let $zoneRef := concat('#', $zone/@xml:id)
(:
: The first predicate with `contains` is just a rough estimate to narrow down the result set.
: It uses the index and is fast while the second (exact) predicate is generally too slow
:)
let $measures := $mei//mei:measure[contains(@facs, $zoneRef)][$zoneRef = tokenize(@facs, '\s+')]
return
for $measure in $measures
let $measureLabel := measure:getMeasureLabel($measure)
return
map {
'zoneId': $zone/string(@xml:id),
'ulx': $zone/string(@ulx),
'uly': $zone/string(@uly),
'lrx': $zone/string(@lrx),
'lry': $zone/string(@lry),
'id': $measure/string(@xml:id),
'name': $measureLabel,
'type': $measure/string(@type),
'rest': measure:getMRest($measure)
}
};
4 changes: 4 additions & 0 deletions packages/eoTheme/sass/etc/facsimile.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
@include background-image(radial-gradient(center , rgba(100, 100, 100, 0.3), rgba(0, 0, 0, 0) 5px));
}

.del {
text-decoration: line-through;
}

.measure.highlighted {
box-shadow: 0 0 4px rgba(0, 0, 0, 0.6) inset;
}
Expand Down
Loading
Loading